packages feed

typed-fsm 0.1.0.1 → 0.2.0.0

raw patch · 6 files changed

+187/−106 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- TypedFsm.Driver: Cont :: SomeOperate ps m a -> Result ps (m :: Type -> Type) a
- TypedFsm.Driver: Finish :: a -> Result ps (m :: Type -> Type) a
- TypedFsm.Driver: GenMsg :: (state -> event -> Maybe (SomeMsg ps from)) -> GenMsg ps state event (from :: ps)
- TypedFsm.Driver: NotMatchGenMsg :: Sing t -> Result ps (m :: Type -> Type) a
- TypedFsm.Driver: SomeMsg :: Msg ps from to -> SomeMsg ps (from :: ps)
- TypedFsm.Driver: SomeOperate :: Operate m (At a o) i -> SomeOperate ts (m :: Type -> Type) a
- TypedFsm.Driver: data Result ps (m :: Type -> Type) a
- TypedFsm.Driver: data SomeMsg ps (from :: ps)
- TypedFsm.Driver: data SomeOperate ts (m :: Type -> Type) a
- TypedFsm.Driver: getSomeOperateSt :: forall ts (m :: Type -> Type) a. SingKind ts => SomeOperate ts m a -> Demote ts
- TypedFsm.Driver: newtype GenMsg ps state event (from :: ps)
- TypedFsm.Driver: runOp :: forall ps event state (m :: Type -> Type) a (input :: ps) (output :: ps). (SingI input, GCompare (Sing :: ps -> Type), Monad m) => State2GenMsg ps state event -> [event] -> Operate (StateT state m) (At a output) input -> StateT state m (Result ps (StateT state m) a)
- TypedFsm.Driver: sOrdToGCompare :: forall n (a :: n) (b :: n). SOrd n => Sing a -> Sing b -> GOrdering a b
- TypedFsm.Driver: type Op ps state (m :: Type -> Type) a (o :: ps) (i :: ps) = Operate StateT state m At a o i
- TypedFsm.Driver: type SomeOp ps state (m :: Type -> Type) a = SomeOperate ps StateT state m a
- TypedFsm.Driver: type State2GenMsg ps state event = DMap Sing :: ps -> Type GenMsg ps state event
+ TypedFsm.Driver.Common: AnyMsg :: Msg ps from to -> AnyMsg ps
+ TypedFsm.Driver.Common: Cont :: SomeOperate ps m a -> Result ps e (m :: Type -> Type) a
+ TypedFsm.Driver.Common: ErrorInfo :: e -> Result ps e (m :: Type -> Type) a
+ TypedFsm.Driver.Common: Finish :: a -> Result ps e (m :: Type -> Type) a
+ TypedFsm.Driver.Common: SomeMsg :: Msg ps from to -> SomeMsg ps (from :: ps)
+ TypedFsm.Driver.Common: SomeOperate :: Operate m (At a o) i -> SomeOperate ts (m :: Type -> Type) a
+ TypedFsm.Driver.Common: data AnyMsg ps
+ TypedFsm.Driver.Common: data Result ps e (m :: Type -> Type) a
+ TypedFsm.Driver.Common: data SomeMsg ps (from :: ps)
+ TypedFsm.Driver.Common: data SomeOperate ts (m :: Type -> Type) a
+ TypedFsm.Driver.Common: getSomeOperateSingeton :: forall ts (m :: Type -> Type) a. SingKind ts => SomeOperate ts m a -> Sing ts
+ TypedFsm.Driver.Common: getSomeOperateSt :: forall ts (m :: Type -> Type) a. SingKind ts => SomeOperate ts m a -> Demote ts
+ TypedFsm.Driver.General: UnexpectMsg :: AnyMsg ps -> UnexpectMsg ps
+ TypedFsm.Driver.General: anyToSomeMsg :: forall ps (input :: ps). (SingI input, SEq ps) => AnyMsg ps -> Maybe (SomeMsg ps input)
+ TypedFsm.Driver.General: newtype UnexpectMsg ps
+ TypedFsm.Driver.General: runOperate :: forall ps m a (input :: ps) (output :: ps). (Monad m, SingI input, SEq ps) => [AnyMsg ps] -> Operate m (At a output) input -> m (Result ps (UnexpectMsg ps) m a)
+ TypedFsm.Driver.Op: GenMsg :: (state -> event -> Maybe (SomeMsg ps from)) -> GenMsg ps state event (from :: ps)
+ TypedFsm.Driver.Op: NotFoundGenMsg :: SomeSing ps -> NotFoundGenMsg ps
+ TypedFsm.Driver.Op: newtype GenMsg ps state event (from :: ps)
+ TypedFsm.Driver.Op: newtype NotFoundGenMsg ps
+ TypedFsm.Driver.Op: runOp :: forall ps event state (m :: Type -> Type) a (input :: ps) (output :: ps). (SingI input, GCompare (Sing :: ps -> Type), Monad m) => State2GenMsg ps state event -> [event] -> Operate (StateT state m) (At a output) input -> StateT state m (Result ps (NotFoundGenMsg ps) (StateT state m) a)
+ TypedFsm.Driver.Op: sOrdToGCompare :: forall n (a :: n) (b :: n). SOrd n => Sing a -> Sing b -> GOrdering a b
+ TypedFsm.Driver.Op: type Op ps state (m :: Type -> Type) a (o :: ps) (i :: ps) = Operate StateT state m At a o i
+ TypedFsm.Driver.Op: type SomeOp ps state (m :: Type -> Type) a = SomeOperate ps StateT state m a
+ TypedFsm.Driver.Op: type State2GenMsg ps state event = DMap Sing :: ps -> Type GenMsg ps state event

Files

src/TypedFsm.hs view
@@ -43,8 +43,12 @@   module TypedFsm.Core,    -- * Running FSM-  module TypedFsm.Driver,+  module TypedFsm.Driver.Common,+  module TypedFsm.Driver.General,+  module TypedFsm.Driver.Op, ) where  import TypedFsm.Core-import TypedFsm.Driver+import TypedFsm.Driver.Common+import TypedFsm.Driver.General+import TypedFsm.Driver.Op
− src/TypedFsm/Driver.hs
@@ -1,102 +0,0 @@-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE LambdaCase #-}--{- | Running FSM--The core function is `runOp`, and the other functions are to make it work properly.--}-module TypedFsm.Driver where--import Control.Monad.State as S (MonadState (get), StateT)-import Data.Dependent.Map (DMap)-import Data.Dependent.Map qualified as D-import Data.GADT.Compare (GCompare, GOrdering (..))-import Data.IFunctor (At (..))-import Data.Ord.Singletons (SOrd (sCompare), SOrdering (..))-import Data.Singletons (Sing, SingI (..), SingKind (..))-import TypedFsm.Core (Operate (..), StateTransMsg (Msg))-import Unsafe.Coerce (unsafeCoerce)--data SomeOperate ts m a-  = forall (i :: ts) (o :: ts).-    (SingI i) =>-    SomeOperate (Operate m (At a o) i)--getSomeOperateSt :: (SingKind ts) => SomeOperate ts m a -> Demote ts-getSomeOperateSt (SomeOperate (_ :: Operate m (At a o) i)) = fromSing $ sing @i--{- | Reuslt of runOp--* Finish, return val a-* A wrapper for SomeOperate that returns the remaining computation when there is not enough input-* There is no corresponding GenMsg function defined for some FSM states--}-data Result ps m a-  = Finish a-  | Cont (SomeOperate ps m a)-  | forall t. NotMatchGenMsg (Sing (t :: ps))--{- | `Op` adds new assumptions based on `Operate`: assume that the internal monad contains at least a state monad.--@-type Op ps state m a o i = Operate (StateT state m) (At a (o :: ps)) (i :: ps)-@--`Op` contains two states, `ps` and `state`.--`ps` represents the state of the state machine-`state` represents the internal state.--The external event needs to be converted to Msg.--It is essentially a function `event -> Msg`, but this function is affected by both `ps` and `state`.--}-type Op ps state m a o i = Operate (StateT state m) (At a (o :: ps)) (i :: ps)--newtype GenMsg ps state event from-  = GenMsg (state -> event -> Maybe (SomeMsg ps from))--type State2GenMsg ps state event = DMap (Sing @ps) (GenMsg ps state event)--data SomeMsg ps from-  = forall (to :: ps).-    (SingI to) =>-    SomeMsg (Msg ps from to)--type SomeOp ps state m a = SomeOperate ps (StateT state m) a--sOrdToGCompare-  :: forall n (a :: n) (b :: n)-   . (SOrd n)-  => Sing a -> Sing b -> GOrdering a b-sOrdToGCompare a b = case sCompare a b of-  SEQ -> unsafeCoerce GEQ-  SLT -> GLT-  SGT -> GGT--runOp-  :: forall ps event state m a (input :: ps) (output :: ps)-   . ( SingI input-     , GCompare (Sing @ps)-     )-  => (Monad m)-  => State2GenMsg ps state event-  -> [event]-  -> Operate (StateT state m) (At a output) input-  -> (StateT state m) (Result ps (StateT state m) a)-runOp dmp evns = \case-  IReturn (At a) -> pure (Finish a)-  LiftM m -> m Prelude.>>= runOp dmp evns-  In f -> do-    let singInput = sing @input-    case D.lookup singInput dmp of-      Nothing -> pure (NotMatchGenMsg singInput)-      Just (GenMsg genMsg) -> loop evns-       where-        loop [] = pure $ Cont $ SomeOperate (In f)-        loop (et : evns') = do-          state' <- get-          case genMsg state' et of-            Nothing -> loop evns'-            Just (SomeMsg msg) -> runOp dmp evns' (f msg)
+ src/TypedFsm/Driver/Common.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-unused-do-bind #-}++module TypedFsm.Driver.Common where++import Data.IFunctor (At (..))+import Data.Singletons (Sing, SingI (..), SingKind (..))+import TypedFsm.Core (Operate (..), StateTransMsg (Msg))+import Unsafe.Coerce (unsafeCoerce)++data SomeOperate ts m a+  = forall (i :: ts) (o :: ts).+    (SingI i) =>+    SomeOperate (Operate m (At a o) i)++getSomeOperateSingeton :: (SingKind ts) => SomeOperate ts m a -> Sing ts+getSomeOperateSingeton (SomeOperate (_ :: Operate m ia i)) =+  unsafeCoerce $ sing @i++getSomeOperateSt :: (SingKind ts) => SomeOperate ts m a -> Demote ts+getSomeOperateSt (SomeOperate (_ :: Operate m ia i)) = fromSing $ sing @i++data SomeMsg ps from+  = forall (to :: ps).+    (SingI to) =>+    SomeMsg (Msg ps from to)++data AnyMsg ps+  = forall (from :: ps) (to :: ps).+    (SingI from, SingI to) =>+    AnyMsg (Msg ps from to)++{- | Reuslt of run FSM++* Finish, return val a+* A wrapper for SomeOperate that returns the remaining computation when there is not enough input+* Error happened+-}+data Result ps e m a+  = Finish a+  | Cont (SomeOperate ps m a)+  | ErrorInfo e
+ src/TypedFsm/Driver/General.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-unused-do-bind #-}++-- | Running FSM+module TypedFsm.Driver.General where++import Data.Bool.Singletons (SBool (..))+import Data.Eq.Singletons (SEq (..))+import Data.IFunctor (At (..))+import Data.Singletons (SingI (..))+import TypedFsm.Core (Operate (..), StateTransMsg (Msg))+import TypedFsm.Driver.Common+import Unsafe.Coerce (unsafeCoerce)++anyToSomeMsg+  :: forall ps input+   . (SingI input, SEq ps)+  => AnyMsg ps -> Maybe (SomeMsg ps input)+anyToSomeMsg (AnyMsg (msg :: Msg ps from to)) =+  case sing @from %== sing @input of+    -- (from == input) ~ True+    -- ==> from ~ input+    STrue -> unsafeCoerce (Just (SomeMsg msg))+    SFalse -> Nothing++newtype UnexpectMsg ps = UnexpectMsg (AnyMsg ps)++runOperate+  :: forall ps m a (input :: ps) (output :: ps)+   . ( Monad m+     , SingI input+     , SEq ps+     )+  => [AnyMsg ps]+  -> Operate m (At a output) input+  -> m (Result ps (UnexpectMsg ps) m a)+runOperate anyMsgs = \case+  IReturn (At a) -> pure (Finish a)+  LiftM m -> m >>= (runOperate anyMsgs)+  In f -> loop anyMsgs+   where+    loop [] = pure $ Cont $ SomeOperate (In f)+    loop (anyMsg : evns') = do+      case anyToSomeMsg @_ @input anyMsg of+        Nothing -> pure (ErrorInfo $ UnexpectMsg anyMsg)+        Just (SomeMsg msg) -> runOperate evns' (f msg)
+ src/TypedFsm/Driver/Op.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -Wno-unused-do-bind #-}++{- | Running FSM++The core function is `runOp`, and the other functions are to make it work properly.+-}+module TypedFsm.Driver.Op where++import Control.Monad.State as S (MonadState (get), StateT)+import Data.Dependent.Map (DMap)+import Data.Dependent.Map qualified as D+import Data.GADT.Compare (GCompare, GOrdering (..))+import Data.IFunctor (At (..))+import Data.Ord.Singletons (SOrd (sCompare), SOrdering (..))+import Data.Singletons (Sing, SingI (..), SomeSing (..))+import TypedFsm.Core (Operate (..))+import TypedFsm.Driver.Common+import Unsafe.Coerce (unsafeCoerce)++{- | `Op` adds new assumptions based on `Operate`: assume that the internal monad contains at least a state monad.++@+type Op ps state m a o i = Operate (StateT state m) (At a (o :: ps)) (i :: ps)+@++`Op` contains two states, `ps` and `state`.++`ps` represents the state of the state machine+`state` represents the internal state.++The external event needs to be converted to Msg.++It is essentially a function `event -> Msg`, but this function is affected by both `ps` and `state`.+-}+type Op ps state m a o i = Operate (StateT state m) (At a (o :: ps)) (i :: ps)++type SomeOp ps state m a = SomeOperate ps (StateT state m) a++newtype GenMsg ps state event from+  = GenMsg (state -> event -> Maybe (SomeMsg ps from))++type State2GenMsg ps state event = DMap (Sing @ps) (GenMsg ps state event)++sOrdToGCompare+  :: forall n (a :: n) (b :: n)+   . (SOrd n)+  => Sing a -> Sing b -> GOrdering a b+sOrdToGCompare a b = case sCompare a b of+  SEQ -> unsafeCoerce GEQ+  SLT -> GLT+  SGT -> GGT++newtype NotFoundGenMsg ps = NotFoundGenMsg (SomeSing ps)++runOp+  :: forall ps event state m a (input :: ps) (output :: ps)+   . ( SingI input+     , GCompare (Sing @ps)+     )+  => (Monad m)+  => State2GenMsg ps state event+  -> [event]+  -> Operate (StateT state m) (At a output) input+  -> (StateT state m) (Result ps (NotFoundGenMsg ps) (StateT state m) a)+runOp dmp evns = \case+  IReturn (At a) -> pure (Finish a)+  LiftM m -> m >>= runOp dmp evns+  In f -> do+    let singInput = sing @input+    case D.lookup singInput dmp of+      Nothing -> pure (ErrorInfo $ NotFoundGenMsg $ SomeSing singInput)+      Just (GenMsg genMsg) -> loop evns+       where+        loop [] = pure $ Cont $ SomeOperate (In f)+        loop (et : evns') = do+          state' <- get+          case genMsg state' et of+            Nothing -> loop evns'+            Just (SomeMsg msg) -> runOp dmp evns' (f msg)
typed-fsm.cabal view
@@ -20,7 +20,7 @@ -- PVP summary:     +-+------- breaking API changes --                  | | +----- non-breaking API additions --                  | | | +--- code changes with no API change-version:            0.1.0.1+version:            0.2.0.0  -- A short (one-line) description of the package. synopsis: A framework for strongly typed FSM@@ -70,7 +70,9 @@     -- Modules exported by the library.     exposed-modules: TypedFsm                    , TypedFsm.Core-                   , TypedFsm.Driver+                   , TypedFsm.Driver.Common+                   , TypedFsm.Driver.Op+                   , TypedFsm.Driver.General                    , Data.IFunctor      -- Modules included in this library but not exported.