Estoy tratando de definir una familia de máquinas de estados con tipos de estados algo diferentes. En particular, las máquinas de estado más "complejas" tienen estados que se forman combinando los estados de máquinas de estado más simples.
(Esto es similar a una configuración orientada a objetos donde un objeto tiene varios atributos que también son objetos).
Aquí hay un ejemplo simplificado de lo que quiero lograr.
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
En términos más generales, quiero un marco generalizado donde estos anidamientos sean más complejos. Aquí hay algo que deseo saber hacer.
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
Para el contexto, esto es lo que quiero lograr con esta maquinaria:
Quiero diseñar estas cosas llamadas "Transformadores de flujo", que son básicamente funciones con estado: consumen un token, mutan su estado interno y generan algo. Específicamente, estoy interesado en una clase de Transformadores de Stream donde la salida es un valor booleano; llamaremos a estos "monitores".
Ahora, estoy tratando de diseñar combinadores para estos objetos. Algunos de ellos son:
- Un
pre
combinador Supongamos quemon
es un monitor. Entonces,pre mon
es un monitor que siempre produceFalse
después de que se consume el primer token y luego imita el comportamiento demon
como si el token anterior se estuviera insertando ahora. Me gustaría modelar el estado depre mon
withStateWithTrigger
en el ejemplo anterior ya que el nuevo estado es booleano junto con el estado original. - Un
and
combinador Supongamos quem1
ym2
son monitores. Luego,m1 `and` m2
es un monitor que alimenta el token a m1, y luego a m2, y luego produceTrue
si ambas respuestas fueron verdaderas. Me gustaría modelar el estado dem1 `and` m2
withCombinedState
en el ejemplo anterior ya que se debe mantener el estado de ambos monitores.
fuente
_innerVal <$> get
es justogets _innerVal
(comogets f == liftM f get
, yliftM
estáfmap
especializado para mónadas).StateT InnerState m Int
valor en primer lugarouterStateFoo
?zoom
sirve.Respuestas:
Para su primera pregunta, como Carl mencionó,
zoom
delens
hace exactamente lo que quiere. Su código con lentes podría escribirse así:Editar: mientras estamos en eso, si ya está trayendo
lens
,innerStateFoo
puede escribirse así:fuente
Creo que lo que quieres lograr no necesita mucha maquinaria.
Esto
StreamTransformer
no es necesariamente con estado, pero admite los con estado. No es necesario (¡y la OMI no debería! ¡En la mayoría de los casos!) Buscar clases de tipos para definirlas (¡o incluso alguna vez! :) pero ese es otro tema).fuente
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'))
.