Puede hacer que sus funciones sean monad-agnósticas mediante el uso de clases de tipos en lugar de pilas de mónadas concretas.
Digamos que usted tiene esta función, por ejemplo:
bangMe :: State String()
bangMe = do
str <- get
put $ str ++ "!"
-- or just modify (++"!")
Por supuesto, se da cuenta de que funciona como un transformador, así que se podría escribir:
bangMe :: Monad m => StateT String m()
Sin embargo, si tienes una función que usa una pila diferente, digamos ReaderT [String] (StateT String IO)()
o lo que sea, tendrás que usar la temida función lift
! Entonces, ¿cómo se evita eso?
El truco es hacer que la firma de la función sea aún más genérica, por lo que dice que la mónada State
puede aparecer en cualquier lugar de la pila de mónadas. Esto se hace así:
bangMe :: MonadState String m => m()
Esto obliga m
ser una mónada que soporta estado (casi) cualquier lugar en la pila mónada, y la función de este modo trabajar sin levantar para cualquier pila.
Sin embargo, hay un problema; dado que IO
no es parte del mtl
, no tiene un transformador (por ejemplo, IOT
) ni una clase de tipo útil por defecto. Entonces, ¿qué debe hacer cuando quiere levantar acciones de IO arbitrariamente?
¡Al rescate viene MonadIO
!Se comporta casi de manera idéntica a MonadState
, MonadReader
etc., la única diferencia es que tiene un mecanismo de elevación ligeramente diferente. Funciona de esta manera: puede tomar cualquier acción IO
y usar liftIO
para convertirla en una versión independiente de mónada. Por lo tanto:
action :: IO()
liftIO action :: MonadIO m => m()
Mediante la transformación de todas las acciones monádicos que desea utilizar de esta manera, se puede entrelazar mónadas tanto como usted desee sin ningún trabajo tedioso.
Oh, creo que me siento estúpido, mencionaste que en una de tus respuestas anteriores, no podía entenderlo en ese momento. Ahora, ¡gracias! – aelguindy