Evitar la elevación con transformadores de mónada


Tengo un problema para que una pila de transformadores de mónada (o incluso un transformador de mónada) sobre IO. Todo está bien, excepto que el uso de ascensor antes de cada acción es terriblemente molesto! Sospecho que realmente no hay nada que hacer al respecto, pero pensé en preguntar de todos modos.

Soy consciente de levantar bloques enteros, pero ¿qué pasa si el código es realmente de tipos mixtos? ¿No sería bueno si GHC lanzó un poco de azúcar sintáctica (por ejemplo, <-$ = <- lift)?

Author: duplode, 2012-01-29

2 answers

Para todas las mónadas estándar mtl, no necesitas lift en absoluto. get, put, ask, tell - todos trabajan en cualquier mónada con el transformador correcto en algún lugar de la pila. La pieza que falta es IO, e incluso allí liftIO levanta una acción de IO arbitraria hacia abajo un número arbitrario de capas.

Esto se hace con typeclasses para cada "efecto" en oferta: por ejemplo, MonadState proporciona get y put. Si desea crear su propia envoltura newtype alrededor de un transformador pila, puede hacer deriving (..., MonadState MyState, ...) con la extensión GeneralizedNewtypeDeriving, o rodar su propia instancia:

instance MonadState MyState MyMonad where
  get = MyMonad get
  put s = MyMonad (put s)

Puede usar esto para exponer u ocultar selectivamente componentes de su transformador combinado, definiendo algunas instancias y no otras.

(Puede extender fácilmente este enfoque a los efectos monádicos completamente nuevos que defina usted mismo, definiendo su propia clase de tipo y proporcionando instancias repetitivas para los transformadores estándar, pero las mónadas completamente nuevas son raras; la mayoría de las veces, lo conseguirá simplemente componiendo el conjunto estándar ofrecido por mtl.)

 54
Author: ehird,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-29 16:56:53

Puede hacer que sus funciones sean agnósticas usando typeclass en lugar de pilas de mónadas concretas.

Digamos que tienes esta función, por ejemplo:{[17]]}

bangMe :: State String ()
bangMe = do
  str <- get
  put $ str ++ "!"
  -- or just modify (++"!")

Por supuesto, te das cuenta de que funciona como un transformador, así que uno podría escribir: {[17]]}

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 el firma de función 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 a m a ser una mónada que soporta el estado (virtualmente) en cualquier lugar de la pila de mónadas, y la función funcionará sin levantar ninguna pila de este tipo.

Hay un problema, sin embargo; dado que IO no es parte de mtl, no tiene un transformador (por ejemplo, IOT) ni una clase de tipo útil por defecto. Entonces, ¿qué debe ¿cuándo desea 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 así: puedes tomar cualquier acción IO, y usar liftIO para convertirla en una versión agnóstica de la mónada. Así que:

action :: IO ()
liftIO action :: MonadIO m => m ()

Al transformar todas las acciones monádicas que desea utilizar de esta manera, puede entrelazar mónadas tanto como desee sin ninguna levantamiento tedioso.

 46
Author: dflemstr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-29 16:48:12