Estou tentando definir uma família de máquinas de estado com tipos um pouco diferentes de estados. Em particular, as máquinas de estados mais "complexas" possuem estados formados pela combinação dos estados de máquinas de estados mais simples.
(É semelhante a uma configuração orientada a objetos, na qual um objeto possui vários atributos que também são objetos.)
Aqui está um exemplo simplificado do que eu quero alcançar.
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
De um modo mais geral, quero uma estrutura generalizada em que esses aninhamentos sejam mais complexos. Aqui está algo que eu gostaria de saber como fazer.
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
Por contexto, é isso que eu quero alcançar com este mecanismo:
Quero projetar essas coisas chamadas "Stream Transformers", que são basicamente funções com estado: elas consomem um token, modificam seu estado interno e produzem algo. Especificamente, estou interessado em uma classe de Stream Transformers em que a saída é um valor booleano; chamaremos esses "monitores".
Agora, estou tentando projetar combinadores para esses objetos. Alguns deles são:
- Um
precombinador. Suponha quemonseja um monitor. Então,pre moné um monitor que sempre produzFalsedepois que o primeiro token é consumido e imita o comportamentomoncomo se o token anterior estivesse sendo inserido agora. Gostaria de modelar o estado depre moncomStateWithTriggerno exemplo acima, pois o novo estado é um booleano junto com o estado original. - Um
andcombinador. Suponha quem1em2sejam monitores. Então,m1 `and` m2é um monitor que alimenta o token para m1, e depois para m2, e produzTruese as duas respostas forem verdadeiras. Gostaria de modelar o estado dem1 `and` m2comCombinedStateno exemplo acima, pois o estado de ambos os monitores deve ser mantido.
StateT InnerState m Intvalor em primeiro lugar outerStateFoo?
_innerVal <$> geté apenasgets _innerVal(asgets f == liftM f get, eliftMéfmapespecializado apenas em mônadas).