hacer - haskell ejemplos
Plegado, composición de funciones, mónadas y pereza, ¿oh mi? (2)
Estoy confundido. Puedo escribir esto
import Control.Monad
main = print $ head $ (foldr (.) id [f, g]) [3]
where f = (1:)
g = undefined
y la salida es 1
. Eso tiene sentido, porque 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 utilizo una técnica monádica vagamente similar, no funciona igual:
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 esperaría que se redujera:
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 la composición:
import Control.Monad
main = print $ (foldr (>=>) return [f, g]) 3
where f = const Nothing
g = undefined
cumple el corto circuito esperado y no 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 comparar los dos enfoques podría haber sido comparar manzanas y naranjas, pero ¿puedes explicar la diferencia? Pensé que f <=< g
era el análogo monádico de f . g
f . g
, pero aparentemente no son tan análogos como pensaba. ¿Puedes explicar porque?
Depende de con qué mónada esté trabajando y de cómo esté definido 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: perezosa.
> 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 una variante perezosa como una estricta.
> Control.Monad.Writer.runWriter foo
(42,())
> Control.Monad.Writer.Strict.runWriter foo
*** Exception: Prelude.undefined
Estado: Tiene también una versión tanto estricta como perezosa.
> 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
El vínculo para Maybe
es estricto en el primer argumento.
Just v >>= f = f v
Nothing >>= f = Nothing
Así que cuando intentas
Just v >>= undefined >>= /_ -> Nothing
tu golpeas
undefined v >>= /_ -> Nothing
y la implementación debe averiguar si undefined v
es Nothing
o Just something
para ver qué ecuación de (>>=)
usar.
Por otra parte,
Nothing >>= undefined
determina el resultado sin mirar el segundo argumento de (>>=)
.