J'essaie de définir une famille de machines à états avec des types d'états quelque peu différents. En particulier, les machines à états les plus "complexes" ont des états qui sont formés en combinant les états des machines à états plus simples.
(Ceci est similaire à un paramètre orienté objet où un objet a plusieurs attributs qui sont également des objets.)
Voici un exemple simplifié de ce que je veux réaliser.
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
Plus généralement, je souhaite un cadre généralisé où ces emboîtements sont plus complexes. Voici quelque chose que je souhaite savoir faire.
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
Pour le contexte, voici ce que je veux réaliser avec cette machine:
Je veux concevoir ces choses appelées "Stream Transformers", qui sont essentiellement des fonctions avec état: elles consomment un jeton, modifient leur état interne et produisent quelque chose. Plus précisément, je suis intéressé par une classe de transformateurs de flux où la sortie est une valeur booléenne; nous appellerons ces "moniteurs".
Maintenant, j'essaie de concevoir des combinateurs pour ces objets. Certains d'entre eux sont:
- Un
pre
combinateur. Supposons que cemon
soit un moniteur. Ensuite,pre mon
est un moniteur qui produit toujoursFalse
après la consommation du premier jeton, puis imite le comportementmon
comme si le jeton précédent était inséré maintenant. Je voudrais modéliser l'état depre mon
avecStateWithTrigger
dans l'exemple ci-dessus car le nouvel état est un booléen avec l'état d'origine. - Un
and
combinateur. Supposons quem1
etm2
sont moniteurs. Ensuite,m1 `and` m2
est un moniteur qui envoie le jeton à m1, puis à m2, puis produitTrue
si les deux réponses sont vraies. Je voudrais modéliser l'état dem1 `and` m2
avecCombinedState
dans l'exemple ci-dessus car l'état des deux moniteurs doit être maintenu.
la source
_innerVal <$> get
est justegets _innerVal
(commegets f == liftM f get
, etliftM
est justefmap
spécialisé aux monades).StateT InnerState m Int
valeur en premier lieuouterStateFoo
?zoom
sert.Réponses:
Pour votre première question, comme Carl l'a mentionné,
zoom
delens
fait exactement ce que vous voulez. Votre code avec des lentilles pourrait être écrit comme ceci:Edit: Pendant que nous y sommes, si vous apportez déjà,
lens
alorsinnerStateFoo
peut être écrit comme suit:la source
Je pense que ce que vous voulez réaliser n'a pas besoin de beaucoup de machines.
Ce
StreamTransformer
n'est pas nécessairement avec état, mais admet ceux avec état. Vous n'avez pas besoin (et l'OMI ne devrait pas! Dans la plupart des cas !!) d'atteindre les classes de caractères afin de les définir (ou même jamais! :) mais c'est un autre sujet).la source
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'))
.