2011-02-07 12 views
10

Esto debería ser fácil para los profesionales Haskell ..¿Qué es similar a fmap para los valores monádicos?

Tengo un valor Tal vez,

> let a = Just 5 

puedo imprimirlo:

> print a 
Just 5 

Pero quiero aplicar un I/O acción al interior de Maybe. La única forma que he descubierto la manera de hacer esto sin usar case es:

> maybe (return()) print a 
5 

Sin embargo, esto parece demasiado prolijo. En primer lugar, return() es específico para la mónada de E/S, así que tengo que encontrar un "cero" diferente para cada mónada. Quiero probar este truco.

Quiero asignar un mapa de una E/S Acción (imprimir) en el valor Maybe e imprimirlo si es Just, o no hacer nada si es Nothing. Quiero expresarlo de alguna manera como,

> fmap print a 

Pero esto no funciona ya print es una acción IO:

No instance for (Show (IO())) 

Probé Applicative, pero no puedo averiguar si hay una manera de expresarlo:

> print <$> a 
No instance for (Show (IO())) 

Obviamente estoy un poco confundido acerca de mónadas-dentro-mónadas .. puede alguien decirme la manera correcta de manera más sucinta expresan esto?

Gracias.

+2

Btw 'print <$> a' es exactamente lo mismo que' fmap print a', –

Respuesta

23

La respuesta de pelotom es la más sencilla. ¡Pero no el divertido! sequence es la función de Haskell que se puede considerar como invertir el orden de los constructores de tipo entre una lista y una mónada.

sequence :: (Monad m) => [m a] -> m [a]

Ahora lo que quiere es, por así decirlo, a darle la vuelta al orden de constructores de tipos entre un Maybe y una mónada. Data.Traventable exporta una función sequence con solo esa capacidad.

Data.Traversable.sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)

Esto puede especializar a Maybe (IO()) -> IO (Maybe()) como en su ejemplo.

Por lo tanto:

Prelude Data.Traversable> Data.Traversable.sequence (fmap print $ Nothing) 
Nothing 

Prelude Data.Traversable> Data.Traversable.sequence (fmap print $ Just 123) 
123 
Just() 

Tenga en cuenta que también hay una función sequenceA que es ligeramente más general, trabajando no sólo en mónadas, pero todos los aplicativos.

¿Por qué utilizar este enfoque? Para Maybe, el enfoque que lo desmonta explícitamente está bien. ¿Pero qué pasa con una estructura de datos más grande, por ejemplo, una Map? En ese caso, traverse, sequenceA y amigos de Data.Traversable pueden ser realmente útiles.

Edición: como notas de Ed'ka, traverse :: Applicative f => (a -> f b) -> t a -> f (t b) y así uno solo puede escribir traverse print $ Just 123.

+0

+1 para la respuesta divertida :) –

+9

Puede simplemente usar 'poligonal' $ 123 'con el mismo resultado –

+0

La fuente proporciona una definición simple para poligonal: 'poligonal f = secuenciaA. fmap f' –

16

En primer lugar, el retorno() es específica a la mónada de E/S, así que tengo que llegar a un "cero" diferente para cada mónada quiero intentar este truco en.

return() es en realidad bastante genérica, como puede verse por su tipo:

Prelude> :t return() 
return() :: (Monad m) => m() 

veo nada malo con el enfoque maybe (return()) print a.

0

¿Has probado esto?

unwrap :: (Show a) => Maybe a -> IO() 
unwrap Nothing = return() 
unwrap (Just a) = print a 

Devolverá/imprimirá los datos enviados, después de desenvolverlo.

+0

¿En qué módulo está? – Steve

+0

@Steve - está definiendo su propia función 'unwrap' –

+0

Por cierto, esto imprimirá"() "en el caso donde el argumento es' Nada' ... Creo que la intención del OP era no hacer nada en ese caso –

2

Pero quiero aplicar una acción de E/S al interior de Maybe.

Esto se puede lograr con los transformadores de mónada.

MaybeT es una mónada que puede envolverse alrededor de otra mónada. Dicho de otra manera, MaybeT puede usar cualquier otra mónada para abstraer el error (inocente [1]) en un cálculo.

Lamentablemente, GHCi no (en 2011) tiene ninguna funcionalidad para hacer jugar con transformadores monad más fácil, pero aquí van:

> :m + Control.Monad.Maybe Control.Monad.Trans 
> let a = Just 5 
> runMaybeT$ do { v <- MaybeT$ return a ; liftIO$ print v } 
5 
Just() 

Para mayor comprensión en profundidad de las mónadas y transformadores mónada, sugiero usted lee otras fuentes en la web. Tenga en cuenta que las mónadas también simplemente envuelven valores.

Trataré de mantenerlo simple. Firmas: m = IO, a = Entero

runMaybeT :: MaybeT m a -> m (Quizás a) - Activa un cálculo en MaybeT IO en un cálculo de IO.

do { - haga notación sin sangría para que se ajuste al indicador de ghci [2].

MaybeT :: m (Quizás a) -> MaybeT m a - Envolver un cálculo de tipo IO (Tal entero).

return a :: IO (Sólo Entero) - Reemplace esto con su cálculo.

ascensor - Ejecute el cálculo en la mónada envuelta. [3]

Just() - El resultado del cálculo. GHCi imprime resultados IO siempre que no sea().

MaybeT no está incluido en MTL, lo que podría tener que instalarlo

cabal install MaybeT 

O considere [1]


[1] Para la transmisión de mensajes de error, así, utilizar MonadError

[2] que sé de entrada de multilínea en GHCi

[3] Use liftIO si necesita IO de una pila de mónadas.

+0

¡Gracias! Realmente quiero familiarizarme más con los transformadores. Ya he leído mucho pero todavía tengo dificultades para entender/saber _cuando_ usarlos. Supongo que aprenderé si solo los utilizo más a menudo, así que tal vez esta sea una buena oportunidad. – Steve

Cuestiones relacionadas