content: {-# LANGUAGE ConstraintKinds       #-}
  {-# LANGUAGE DataKinds             #-}
  {-# LANGUAGE ExplicitNamespaces    #-}
  {-# LANGUAGE FlexibleInstances     #-}
  {-# LANGUAGE KindSignatures        #-}
  {-# LANGUAGE LiberalTypeSynonyms   #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE PolyKinds             #-}
  {-# LANGUAGE RankNTypes            #-}
  {-# LANGUAGE TypeFamilies          #-}
  {-# LANGUAGE TypeOperators         #-}
  {-# LANGUAGE UndecidableInstances  #-}
  -- | Syntactic sugar for 'MonadFSM' types, adding appropriate row
  -- constraints and hiding complexity of the internal implementation.
  module Motor.FSM.Sugar
    ( Action(..)
    , ActionMapping(..)
    , FromActions
    , NoActions
    , Actions
    , OnlyActions
    , Get
    , type (!-->)
    , type (!+)
    , type (!-)
    ) where
  
  import           Data.Kind
  import           Data.Row.Records
  import           GHC.TypeLits     (Symbol)
  
  -- | An 'Action' describes a resource action.
  data Action
    = Add Type
    -- ^ Adds a new resource of the given 'Type'.
    | Remain Type
    -- ^ The existing resource of the given 'Type' remains the same.
    | To Type
         Type
    -- ^ Transitions an existing resource from the first 'Type' to a
    -- resource of the second 'Type'.
    | Delete Type
    -- ^ Deletes an existing resource of the given 'Type'.
  
  -- | Mapping from 'Symbol' to some action 'a'.
  data ActionMapping = (:=) Symbol Action
  
  infixr 5 :=
  
  -- | Translates a list of 'Action's to a 'Row'.
  type family FromActions (as :: [ActionMapping]) (rs :: Row *) (c :: Constraint) :: (Row *, Constraint) where
    FromActions '[] rs c = '( rs, c)
    FromActions ((n ':= 'Add a) ': ts) r c =
      FromActions ts (Extend n a r) ( c
                                    , (Extend n a r .! n) ~ a
                                    )
    FromActions ((n ':= 'Delete a) ': ts) r c =
      FromActions ts (r .- n) ( c
                              , (r .! n) ~ a
                              )
    FromActions ((n ':= 'To a b) ': ts) r c =
      FromActions ts ((n .== b) .// r) ( c
                                       , (r .! n) ~ a
                                       , (((n .== b) .// r) .! n) ~ b
                                       )
    FromActions ((n ':= 'Remain a) ': ts) r c =
      FromActions ts r (c, (r .! n) ~ a)
  
  type NoConstraint = (() :: Constraint)
  
  -- | Alias for 'MonadFSM' that includes no actions.
  type NoActions m (r :: Row *) a = m r r a
  
  -- | Alias for 'MonadFSM' that uses 'FromActions' to construct rows.
  type Actions m as (i :: Row *) a
     = forall o c. (FromActions as i NoConstraint ~ '( o, c), c) =>
                     m i o a
  
  -- | Alias for 'MonadFSM' that uses 'FromActions' to construct rows,
  -- starting from an 'Empty' row, i.e. allowing no /other/ resources.
  type OnlyActions m as a = Actions m as Empty a
  
  -- | Gets an existing resource in state 's'.
  type Get m (r :: Row *) n = m r r (r .! n)
  
  -- | Infix version of 'To'.
  type (!-->) i o = 'To i o
  
  infixl 6 !-->
  
  -- | Add a named resource. Alias of 'Add'.
  type (!+) (n :: Symbol) s = n ':= 'Add s
  
  infix 6 !+
  
  -- | Delete a named resource. Alias of 'Delete'.
  type (!-) (n :: Symbol) s = n ':= 'Delete s
  
  infix 6 !-
  