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
pre
combinador. Suponha quemon
seja um monitor. Então,pre mon
é um monitor que sempre produzFalse
depois que o primeiro token é consumido e imita o comportamentomon
como se o token anterior estivesse sendo inserido agora. Gostaria de modelar o estado depre mon
comStateWithTrigger
no exemplo acima, pois o novo estado é um booleano junto com o estado original. - Um
and
combinador. Suponha quem1
em2
sejam monitores. Então,m1 `and` m2
é um monitor que alimenta o token para m1, e depois para m2, e produzTrue
se as duas respostas forem verdadeiras. Gostaria de modelar o estado dem1 `and` m2
comCombinedState
no exemplo acima, pois o estado de ambos os monitores deve ser mantido.
fonte
_innerVal <$> get
é apenasgets _innerVal
(asgets f == liftM f get
, eliftM
éfmap
especializado apenas em mônadas).StateT InnerState m Int
valor em primeiro lugarouterStateFoo
?zoom
serve.Respostas:
Para a sua primeira pergunta, como Carl mencionou,
zoom
delens
faz exatamente o que você quer. Seu código com lentes pode ser escrito assim:Edit: Enquanto estamos nisso, se você já está trazendo
lens
,innerStateFoo
pode ser escrito assim:fonte
Eu acho que o que você deseja alcançar não precisa de muito maquinário.
Isso
StreamTransformer
não é necessariamente stateful, mas admite os stateful. Você não precisa (e a IMO não deve! Na maioria dos casos !!) buscar classes tipográficas para defini-las (ou mesmo nunca! :), mas esse é outro tópico).fonte
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'))
.