-- | The 'MonadFSM' class is the indexed monad for finite-state
-- machines.
{-# LANGUAGE ConstraintKinds        #-}
{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE GADTs                  #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE RankNTypes             #-}
{-# LANGUAGE RebindableSyntax       #-}
{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE TypeFamilies           #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}
module Motor.FSM.Class
  ( MonadFSM(..)
  , Name (..)
  ) where

import           Control.Monad.Indexed
import           Data.Row.Records
import           GHC.OverloadedLabels
import           GHC.TypeLits          (Symbol)

-- * FSM monad

-- | An indexed monad for finite-state machines, managing the state
-- of named resources.
class IxMonad m => MonadFSM (m :: (Row *) -> (Row *) -> * -> *) where
  -- | Creates a new resource by name.
  new :: Name n -> a -> m r (Extend n a r) ()
  -- | Returns an existing resource.
  get :: HasType n a r => Name n -> m r r a
  -- | Deletes an existing resource named by its 'Name'.
  delete :: Name n -> m r (r .- n) ()
  -- | Replaces the state of an existing resource named by its 'Name'.
  enter
    :: ( r' ~ (n .== b .// r)
       )
    => Name n
    -> b
    -> m r r' ()
  -- | Updates the state, using a pure function, of an existing
  -- resource named by its 'Name'.
  update :: (HasType n a r, Modify n a r ~ r) => Name n -> (a -> a) -> m r r ()
  -- | Embed another 'MonadFSM' computation, with empty resource rows,
  -- in this computation.
  call :: m Empty Empty a -> m r r a

-- | A name of a resource, represented using a 'Symbol'.
data Name (n :: Symbol) where
  Name :: KnownSymbol n => Name n

instance (KnownSymbol n, n ~ n') => IsLabel n (Name n') where
  fromLabel = Name :: Name n'