Складывание, составление функций, монады и лень, о боже - PullRequest
22 голосов
/ 24 ноября 2011

Я озадачен. Я могу написать это:

import Control.Monad

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

и вывод 1. Это имеет смысл, потому что оно сводится к:

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

Но если я использую смутно похожую монадическую технику, она не будет работать так же:

import Control.Monad

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

Это хиты prelude.Undefined. Что странно, потому что я ожидаю, что оно уменьшится:

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

Однако, переворачивая порядок композиции:

import Control.Monad

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

завершает ожидаемое короткое замыкание и производит Nothing.

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

Полагаю, при сравнении двух подходов можно было сравнить яблоки и апельсины, но не могли бы вы объяснить разницу? Я думал, что f <=< g был монадическим аналогом f . g, но они явно не так похожи, как я думал. Вы можете объяснить, почему?

Ответы [ 2 ]

20 голосов
/ 24 ноября 2011

Это зависит от того, с какой монадой вы работаете, и от того, как определен ее оператор (>>=).

В случае Maybe, (>>=) строго в своем первом аргументе, как ДаниэльФишер объяснил.

Вот некоторые результаты для нескольких других монад.

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

Идентичность: Ленивый.

> Control.Monad.Identity.runIdentity foo
42

IO: Строгий.

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

Читатель: Ленивый.

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

Писатель: Имеет как ленивый, так истрогий вариант.

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

Состояние: Имеет как строгую, так и ленивую версию.

> 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 голосов
/ 24 ноября 2011

Привязка для Maybe является строгой в первом аргументе.

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

Так что при попытке

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

вы нажимаете

undefined v >>= \_ -> Nothing

иреализация должна выяснить, является ли undefined v Nothing или Just something, чтобы увидеть, какое уравнение (>>=) использовать.

С другой стороны,

Nothing >>= undefined

определяетрезультат, не глядя на второй аргумент (>>=).

...