Próbuję zdefiniować rodzinę maszyn stanowych z nieco odmiennymi rodzajami stanów. W szczególności bardziej „złożone” maszyny stanów mają stany, które powstają przez połączenie stanów prostszych maszyn stanów.
(Jest to podobne do ustawienia obiektowego, w którym obiekt ma kilka atrybutów, które również są obiektami.)
Oto uproszczony przykład tego, co chcę osiągnąć.
data InnerState = MkInnerState { _innerVal :: Int }
data OuterState = MkOuterState { _outerTrigger :: Bool, _inner :: InnerState }
innerStateFoo :: Monad m => StateT InnerState m Int
innerStateFoo = do
i <- _innerVal <$> get
put $ MkInnerState (i + 1)
return i
outerStateFoo :: Monad m => StateT OuterState m Int
outerStateFoo = do
b <- _outerTrigger <$> get
if b
then
undefined
-- Here I want to "invoke" innerStateFoo
-- which should work/mutate things
-- "as expected" without
-- having to know about the outerState it
-- is wrapped in
else
return 666
Mówiąc bardziej ogólnie, chcę uogólnionego frameworka, w którym zagnieżdżenia te są bardziej złożone. Oto coś, co chcę wiedzieć, jak to zrobić.
class LegalState s
data StateLess
data StateWithTrigger where
StateWithTrigger :: LegalState s => Bool -- if this trigger is `True`, I want to use
-> s -- this state machine
-> StateWithTrigger
data CombinedState where
CombinedState :: LegalState s => [s] -- Here is a list of state machines.
-> CombinedState -- The combinedstate state machine runs each of them
instance LegalState StateLess
instance LegalState StateWithTrigger
instance LegalState CombinedState
liftToTrigger :: Monad m, LegalState s => StateT s m o -> StateT StateWithTrigger m o
liftToCombine :: Monad m, LegalState s => [StateT s m o] -> StateT CombinedState m o
W kontekście chcę to osiągnąć dzięki tej maszynie:
Chcę zaprojektować te rzeczy o nazwie „Transformatory strumieniowe”, które są w zasadzie funkcjami stanowymi: zużywają token, mutują swój stan wewnętrzny i wysyłają coś. W szczególności interesuje mnie klasa transformatorów strumieniowych, w której dane wyjściowe są wartością logiczną; nazywamy te „monitorami”.
Teraz próbuję zaprojektować kombinatory dla tych obiektów. Niektórzy z nich są:
pre
Syntezatora. Załóżmy, żemon
to monitor. Następniepre mon
jest monitorem, który zawsze produkujeFalse
po zużyciu pierwszego tokena, a następnie naśladuje zachowaniemon
tak, jakby poprzedni token był wstawiany teraz. Chciałbym modelować stanpre mon
zStateWithTrigger
w powyższym przykładzie, ponieważ nowy stan jest wartością logiczną wraz ze stanem oryginalnym.and
Syntezatora. Załóżmy, żem1
im2
są monitory. Następniem1 `and` m2
jest monitorem, który podaje token do m1, a następnie do m2, a następnie generuje,True
jeśli obie odpowiedzi są prawdziwe. Chciałbym modelować stanm1 `and` m2
zCombinedState
w powyższym przykładzie, ponieważ stan obu monitorów musi być utrzymany.
StateT InnerState m Int
wartość outerStateFoo
?
_innerVal <$> get
jest po prostugets _innerVal
(gets f == liftM f get
i specjalizuje sięliftM
tylkofmap
w monadach).