2011-11-23 11 views
22

Sono perplesso. Posso scrivere questo:Pieghevole, composizione funzionale, monade e pigrizia, oh mio?

import Control.Monad 

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

e l'uscita è 1. Questo ha senso, perché si riduce 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 

Ma se io uso una tecnica monadica vagamente simile, ma non funziona lo stesso:

import Control.Monad 

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

Questo colpisce prelude.Undefined. Il che è strano, perché mi sarei aspettato di ridurre:

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

Tuttavia, lanciando l'ordine di composizione:

import Control.Monad 

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

vuol realizzare il corto circuito previsto e produce Nothing.

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

Suppongo che a confronto i due approcci potrebbero avere le mele confronto stati e arance, ma si può spiegare la differenza? Ho pensato che f <=< g fosse l'analogo monadico a f . g, ma apparentemente non sono così simili come pensavo. Puoi spiegare perché?

risposta

20

Dipende dalla monade su cui si sta lavorando e dal modo in cui è definito l'operatore (>>=).

Nel caso di Maybe, (>>=) è rigoroso nel suo primo argomento, come ha spiegato Daniel Fischer.

Ecco alcuni risultati per una manciata di altre monadi.

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

Identità: pigro.

> Control.Monad.Identity.runIdentity foo 
42 

IO: Strict.

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

Reader: pigro.

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

Writer: ha sia un pigro e una variante severa.

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

Stato: ha anche sia un rigoroso e una versione pigra.

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

Cont: Strict.

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

Il bind per Maybe è rigido nel primo argomento.

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

Quindi, quando si tenta

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

si colpisce

undefined v >>= \_ -> Nothing 

e l'attuazione ha bisogno di scoprire se è undefined vNothing o Just something per vedere che l'equazione di (>>=) da utilizzare.

D'altra parte,

Nothing >>= undefined 

determina il risultato senza guardare il secondo argomento della (>>=).