2011-11-23 11 views
22

Estoy desconcertado. Puedo escribir esto:Plegable, composición de funciones, mónadas y pereza, ¿verdad?

import Control.Monad 

main = print $ head $ (foldr (.) id [f, g]) [3] 
    where f = (1:) 
     g = undefined 

y la salida es 1. Esto tiene sentido, ya que se reduce a:

main = print $ head $ ((1:) . undefined . id) [3] 
main = print $ head $ (1:) ((undefined . id) [3]) 
main = print $ head $ 1 : ((undefined . id) [3]) 
main = print $ 1 

Pero si uso una técnica monádico vagamente similar, que no funciona de la misma:

import Control.Monad 

main = print $ (foldr (<=<) return [f, g]) 3 
    where f = const Nothing 
     g = undefined 

Esto golpea prelude.Undefined. Lo cual es extraño, porque yo esperaría que reducir:

main = print $ ((const Nothing) <=< undefined <=< return) 3 
main = print $ return 3 >>= undefined >>= (\_ -> Nothing) 
main = print $ Nothing -- nope! instead, undefined makes this blow up 

Sin embargo, cambiando el orden de composición:

import Control.Monad 

main = print $ (foldr (>=>) return [f, g]) 3 
    where f = const Nothing 
     g = undefined 

Qué logra el cortocircuito esperada y produce Nothing.

main = print $ (const Nothing >=> undefined >=> return) 3 
main = print $ (const Nothing 3) >>= undefined >>= return 
main = print $ Nothing >>= undefined >>= return 
main = print $ Nothing 

supongo que la comparación de los dos enfoques que podría haber sido comparar manzanas y naranjas, pero se puede explicar la diferencia? Pensé que f <=< g era el análogo monádico de f . g, pero aparentemente no son tan análogos como pensaba. ¿Puedes explicar porque?

Respuesta

20

Depende de qué mónada estás trabajando y cómo se define su operador (>>=).

En el caso de Maybe, (>>=) es estricto en su primer argumento, como explicó Daniel Fischer.

Aquí hay algunos resultados para un puñado de otras mónadas.

> :set -XNoMonomorphismRestriction 
> let foo = (const (return 42) <=< undefined <=< return) 3 
> :t foo 
foo :: (Num t, Monad m) => m t 

Identidad: perezoso.

> Control.Monad.Identity.runIdentity foo 
42 

IO: estricto.

> foo :: IO Integer 
*** Exception: Prelude.undefined 

Lector: perezoso.

> Control.Monad.Reader.runReader foo "bar" 
42 

escritor: tiene tanto un perezoso y una variante estricta.

> Control.Monad.Writer.runWriter foo 
(42,()) 
> Control.Monad.Writer.Strict.runWriter foo 
*** Exception: Prelude.undefined 

Estado: tiene también un tanto vago una versión estricta y.

> Control.Monad.State.runState foo "bar" 
(42,"*** Exception: Prelude.undefined 
> Control.Monad.State.Strict.runState foo "bar" 
*** Exception: Prelude.undefined 

Cont: estricto.

> Control.Monad.Cont.runCont foo id 
*** Exception: Prelude.undefined 
19

El enlace para Maybe es estricto en el primer argumento.

Just v >>= f = f v 
Nothing >>= f = Nothing 

Así que cuando intenta

Just v >>= undefined >>= \_ -> Nothing 

usted golpea

undefined v >>= \_ -> Nothing 

y la implementación tiene que averiguar si es undefined vNothing o Just something a ver qué ecuación de (>>=) de usar.

Por otro lado,

Nothing >>= undefined 

determina el resultado sin mirar el segundo argumento de (>>=).