2011-11-23 13 views
22

Jestem zdziwiony. Mogę napisać to:Składanie, skład funkcji, monady i lenistwo, o mój?

import Control.Monad 

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

, a wyjście to 1. Ma to sens, ponieważ zmniejsza się:

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

Ale jeśli mogę użyć niejasno podobną technikę jednowartościowy, nie działają tak samo:

import Control.Monad 

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

To uderza prelude.Undefined. Co jest dziwne, bo bym się spodziewał, że w celu zmniejszenia:

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

Jednak odwracanie kolejności składzie:

import Control.Monad 

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

ma osiągnąć oczekiwany zwarcia i produkuje Nothing.

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

Przypuszczam, że porównanie tych dwóch podejść mogły być porównywanie jabłek i pomarańczy, ale można wyjaśnić różnicę? Myślałem, że f <=< g był monadycznym analogiem do f . g, ale najwyraźniej nie są one analogiczne, jak myślałem. Czy możesz mi wytłumaczyć dlaczego?

Odpowiedz

20

To zależy od tego, z którą monadą pracujesz i jak zdefiniowano jej operatora (>>=).

W przypadku Maybe, (>>=) ma ścisły pierwszy argument, jak wyjaśnił Daniel Fischer.

Oto niektóre wyniki dla kilku innych monad.

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

Tożsamość: leniwy.

> Control.Monad.Identity.runIdentity foo 
42 

IO: Strict.

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

Reader: leniwy.

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

Scenariusz: ma zarówno leniwy i ścisła wariant.

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

Stan: Posiada również zarówno surowe i leniwy wersja.

> 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

Wiązanie dla Maybe jest ścisłe w pierwszym argumencie.

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

Więc podczas próby

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

trafisz

undefined v >>= \_ -> Nothing 

i realizacja musi dowiedzieć się, czy undefined v jest Nothing lub Just something aby zobaczyć, które równanie (>>=) użyć.

Z drugiej strony,

Nothing >>= undefined 

determinuje wynik bez patrzenia na drugiego argumentu (>>=).