Ich versuche, eine Familie von Zustandsautomaten mit etwas anderen Arten von Zuständen zu definieren. Insbesondere haben die "komplexeren" Zustandsmaschinen Zustände, die durch Kombinieren der Zustände einfacherer Zustandsmaschinen gebildet werden.
(Dies ähnelt einer objektorientierten Einstellung, bei der ein Objekt mehrere Attribute hat, die auch Objekte sind.)
Hier ist ein vereinfachtes Beispiel dafür, was ich erreichen möchte.
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
Generell möchte ich ein allgemeines Framework, in dem diese Verschachtelungen komplexer sind. Hier ist etwas, das ich wissen möchte, wie es geht.
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
Für den Kontext ist dies das, was ich mit dieser Maschinerie erreichen möchte:
Ich möchte diese Dinge entwerfen, die als "Stream-Transformatoren" bezeichnet werden und im Grunde genommen statusbehaftete Funktionen sind: Sie verbrauchen einen Token, mutieren ihren internen Zustand und geben etwas aus. Insbesondere interessiert mich eine Klasse von Stream-Transformatoren, bei denen die Ausgabe ein Boolescher Wert ist. Wir werden diese "Monitore" nennen.
Jetzt versuche ich, Kombinatoren für diese Objekte zu entwerfen. Einige von ihnen sind:
- Ein
pre
Kombinator. Angenommen, dasmon
ist ein Monitor. Dannpre mon
handelt es sich um einen Monitor, der immerFalse
nach dem Verbrauch des ersten Tokens erzeugt und dann das Verhalten nachahmt,mon
als würde das vorherige Token jetzt eingefügt. Ich möchte den Zustand vonpre mon
mitStateWithTrigger
im obigen Beispiel modellieren, da der neue Zustand zusammen mit dem ursprünglichen Zustand ein Boolescher Wert ist. - Ein
and
Kombinator. Nehmen wir an ,m1
undm2
sind Monitore. Dannm1 `and` m2
ist ein Monitor, der das Token an m1 und dann an m2 weiterleitet und dann erzeugt,True
wenn beide Antworten wahr sind. Ich möchte den Status vonm1 `and` m2
mitCombinedState
im obigen Beispiel modellieren, da der Status beider Monitore beibehalten werden muss.
StateT InnerState m Int
Wert outerStateFoo
?
_innerVal <$> get
ist gerechtgets _innerVal
(wiegets f == liftM f get
undliftM
ist nurfmap
auf Monaden spezialisiert).