Складывание, состав функций, монады и лень, о мой?
Я озадачен. Я могу написать это:
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
, но они, по-видимому, не так похожи, как я думал. Можете ли вы объяснить, почему?
Ответы
Ответ 1
Это зависит от того, с какой монадой вы работаете, и как определяется ее оператор (>>=)
.
В случае Maybe
, (>>=)
строго в своем первом аргументе, как объяснил Даниэль Фишер.
Вот некоторые результаты для нескольких других монад.
> :set -XNoMonomorphismRestriction
> let foo = (const (return 42) <=< undefined <=< return) 3
> :t foo
foo :: (Num t, Monad m) => m t
Идентичность: Lazy.
> Control.Monad.Identity.runIdentity foo
42
IO: Строго.
> foo :: IO Integer
*** Exception: Prelude.undefined
Читатель: Lazy.
> 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: Строго.
> Control.Monad.Cont.runCont foo id
*** Exception: Prelude.undefined
Ответ 2
Связывание для Maybe
строго по первому аргументу.
Just v >>= f = f v
Nothing >>= f = Nothing
Итак, когда вы пытаетесь
Just v >>= undefined >>= \_ -> Nothing
вы нажали
undefined v >>= \_ -> Nothing
и для реализации необходимо выяснить, есть ли undefined v
Nothing
или Just something
, какое уравнение (>>=)
использовать.
С другой стороны,
Nothing >>= undefined
определяет результат, не глядя на второй аргумент (>>=)
.