haskell state monads

haskell - Combina estado con acciones de IO.



state monads (2)

El enfoque básico sería reescribir su mónada Op como un transformador de mónada. Esto le permitiría usarlo en una "pila" de mónadas, cuya parte inferior podría ser IO .

Aquí hay un ejemplo de cómo se vería eso:

import Data.Array import Control.Monad.Trans data Registers = Reg { foo :: Int } data ST = ST {registers :: Registers, memory :: Array Int Int} newtype Op m a = Op {runOp :: ST -> m (ST, a)} instance Monad m => Monad (Op m) where return a = Op $ /st -> return (st, a) (>>=) stf f = Op $ /st -> do (st1, a1) <- runOp stf st (st2, a2) <- runOp (f a1) st1 return (st2, a2) instance MonadTrans Op where lift m = Op $ /st -> do a <- m return (st, a) getState :: Monad m => (ST -> a) -> Op m a getState g = Op $ /st -> return (st, g st) updState :: Monad m => (ST -> ST) -> Op m () updState g = Op $ /st -> return (g st, ()) testOpIO :: Op IO String testOpIO = do x <- lift getLine return x test = runOp testOpIO

Las cosas clave a observar:

  • El uso de la clase MonadTrans
  • El uso de la función de lift que actúa sobre getLine , que se utiliza para llevar la función getline desde la mónada IO mónada Op IO .

Por cierto, si no desea que la mónada IO esté siempre presente, puede reemplazarla con la mónada Identity en Control.Monad.Identity . La mónada Op Identity comporta exactamente igual que la mónada Op original.

Supongamos que tengo una mónada estatal como:

data Registers = Reg {...} data ST = ST {registers :: Registers, memory :: Array Int Int} newtype Op a = Op {runOp :: ST -> (ST, a)} instance Monad Op where return a = Op $ /st -> (st, a) (>>=) stf f = Op $ /st -> let (st1, a1) = runOp stf st (st2, a2) = runOp (f a1) st1 in (st2, a2)

con funciones como

getState :: (ST -> a) -> Op a getState g = Op (/st -> (st, g st) updState :: (ST -> ST) -> Op () updState g = Op (/st -> (g st, ()))

Etcétera. Quiero combinar varias operaciones en esta mónada con acciones de IO. Por lo tanto, podría escribir un ciclo de evaluación en el que se realizaron las operaciones en esta mónada y se ejecutó una acción de E / S con el resultado o, creo, debería poder hacer algo como lo siguiente:

newtype Op a = Op {runOp :: ST -> IO (ST, a)}

Las funciones de impresión tendrían el tipo Op () y otras funciones tendrían el tipo Op a, por ejemplo, podría leer un carácter del terminal utilizando una función de tipo IO Char. Sin embargo, no estoy seguro de qué aspecto tendría esa función, ya que, por ejemplo, la siguiente no es válida.

runOp (do x <- getLine; setMem 10 ... (read x :: Int) ... ) st

ya que getLine tiene el tipo IO Char, pero esta expresión tendría el tipo Op Char. En resumen, ¿cómo haría esto?


Usar liftIO

¡Ya estás muy cerca! Tu sugerencia

newtype Op a = Op {runOp :: ST -> IO (ST, a)}

Es excelente y el camino por recorrer.

Para poder ejecutar getLine en un contexto Op , necesita ''levantar'' la operación IO en la mónada Op . Puedes hacer esto escribiendo una función liftIO :

liftIO :: IO a -> Op a liftIO io = Op $ /st -> do x <- io return (st, x)

Ahora puedes escribir:

runOp (do x <- liftIO getLine; ...

Usa la clase MonadIO

Ahora, el patrón de elevar una acción IO en una mónada personalizada es tan común que existe una clase de tipo estándar para ella:

import Control.Monad.Trans class Monad m => MonadIO m where liftIO :: IO a -> m a

Para que su versión de liftIO convierta en una instancia de MonadIO en MonadIO lugar:

instance MonadIO Op where liftIO = ...

Usa StateT

Actualmente ha escrito su propia versión de la mónada estatal, especializada en el estado ST . ¿Por qué no usas la mónada estatal estándar? Le ahorra tener que escribir su propia instancia de Monad , que siempre es la misma para la mónada estatal.

type Op = StateT ST IO

StateT ya tiene una instancia de MonadIO y una instancia de MonadIO , por lo que puede usarlas inmediatamente.

Transformadores de mónada

StateT es un llamado transformador de mónada . Solo desea acciones de IO en su mónada Op , por lo que ya me he especializado con la mónada IO para usted (consulte la definición de type Op ). Pero los transformadores de mónada te permiten apilar mónadas arbitrarias. De esto es de lo que está hablando Inverflow. Puedes leer más sobre ellos here y here .