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.
źródło
_innerVal <$> get
jest po prostugets _innerVal
(gets f == liftM f get
i specjalizuje sięliftM
tylkofmap
w monadach).StateT InnerState m Int
wartośćouterStateFoo
?zoom
służy.Odpowiedzi:
Na swoim pierwszym pytaniu, jak Carl wspomniano,
zoom
zelens
robi dokładnie to, co chcesz. Twój kod z soczewkami można zapisać w następujący sposób:Edit: Skoro już jesteśmy przy nim, jeśli jesteś już przynosząc
lens
następnieinnerStateFoo
można zapisać tak:źródło
Myślę, że to, co chcesz osiągnąć, nie wymaga wiele maszyn.
To
StreamTransformer
nie jest koniecznie stanowe, ale przyznaje te stanowe. Nie musisz (a IMO nie powinno! W większości przypadków !!) sięgać po typy, aby je zdefiniować (a nawet kiedykolwiek! :), ale to już inny temat).źródło
pre st = stateful (Nothing, st) k where k i (s,st) = let (o, st') = runStreamTransformer st i in ( maybe False id s , (Just o, st'))
.