В этом однострочном написании определение mainM
довольно трудно следовать, поэтому я начну с добавления отступа:
mainM =
(\x ->
(\y ->
(\z w -> if w == 0 then (0,y,z) else (x,y,z))
=<< (\x -> x * x))
=<< (+1))
=<< (2/)
Так как monadi c действие должно вернуть монаду, у нас есть эта дополнительная лямбда w -> ...
в определении mainM
. Это дает нам доступ к начальному значению, которое передается в общее вычисление.
Это в значительной степени правильно. Может быть немного легче увидеть, что происходит, если мы не напишем эту функцию как функцию двух аргументов (функция двух аргументов - это функция одного аргумента, которая возвращает функцию):
mainM =
(\x ->
(\y ->
(\z ->
\w -> if w == 0 then (0,y,z) else (x,y,z))
=<< (\x -> x * x))
=<< (+1))
=<< (2/)
Таким образом, результаты применения (2/)
, (+1)
и (\x -> x * x)
к начальному параметру / среде связаны с x
, y
и z
соответственно, и три из них используются для получения конечный результат. Если бы нам хотелось последовательно использовать w
для окружающей среды, определение выглядело бы так:
mainM =
(\x ->
(\y ->
(\z ->
\w -> if w == 0 then (0,y,z) else (x,y,z))
=<< \w -> w * w)
=<< \w -> w + 1)
=<< \w -> 2 / w
Или с использованием (>>=)
вместо (=<<)
:
mainM =
(\w -> 2 / w) >>= \x ->
(\w -> w + 1) >>= \y ->
(\w -> w * w) >>= \z ->
\w -> if w == 0 then (0,y,z) else (x,y,z)
Или, используя do-notation:
mainM = do
x <- \w -> 2 / w
y <- \w -> w + 1
z <- \w -> w * w
\w -> if w == 0 then (0,y,z) else (x,y,z)
В любом случае, обратите внимание, что mainM
на самом деле не использует результаты предыдущих вычислений (то есть x
, y
и * 1035). *) решить, какие вычисления выполнять дальше (то есть какую функцию среды использовать). На самом деле мы можем переписать его, используя только аппликатив:
mainA' = (\w x y z -> if w == 0 then (0,y,z) else (x,y,z))
<*> (2/) <*> (+1) <*> (\x -> x * x)
Использование результатов предыдущего вычисления для выбора следующего будет выглядеть примерно так:
testM = (\x -> if x == 0 then \_ -> 0 else \w -> w / x) =<< subtract 1
GHCi> testM 0.99
-98.99999999999991
GHCi> testM 1
0.0
GHCi> testM 1.01
100.99999999999991
Still монада функция / считыватель не является хорошей иллюстрацией различий между Applicative
и Monad
, потому что (<*>)
и (=<<)
оказываются эквивалентными для нее . Например, с помощью testM
мы можем просто извлечь аргумент окружения из выражения if ...
testM' = (\x w -> if x == 0 then 0 else w / x) =<< subtract 1
..., получая, таким образом, что-то, что просто переписать, используя Applicative
:
testA = (\w x -> if x == 0 then 0 else w / x) <*> subtract 1