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 precombinador Supongamos quemones un monitor. Entonces,pre mones un monitor que siempre produceFalsedespués de que se consume el primer token y luego imita el comportamiento demoncomo si el token anterior se estuviera insertando ahora. Me gustaría modelar el estado depre monwithStateWithTriggeren el ejemplo anterior ya que el nuevo estado es booleano junto con el estado original.
- Un andcombinador Supongamos quem1ym2son monitores. Luego,m1 `and` m2es un monitor que alimenta el token a m1, y luego a m2, y luego produceTruesi ambas respuestas fueron verdaderas. Me gustaría modelar el estado dem1 `and` m2withCombinedStateen el ejemplo anterior ya que se debe mantener el estado de ambos monitores.
fuente

_innerVal <$> getes justogets _innerVal(comogets f == liftM f get, yliftMestáfmapespecializado para mónadas).StateT InnerState m Intvalor en primer lugarouterStateFoo?zoomsirve.Respuestas:
Para su primera pregunta, como Carl mencionó,
zoomdelenshace exactamente lo que quiere. Su código con lentes podría escribirse así:Editar: mientras estamos en eso, si ya está trayendo
lens,innerStateFoopuede escribirse así:fuente
Creo que lo que quieres lograr no necesita mucha maquinaria.
Esto
StreamTransformerno 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')).