函数上下文中 monadic 计算的先前值是多少?
What is the previous value of a monadic computation in the function context?
我正在为 monad 的函数实例苦苦挣扎(谁不会?)。 Monads 可能取决于先前的 monadic 计算的值。这对函数 monad 到底意味着什么?
当我们查看 applicative/monad 个实例的基本实现时
ap f g x = f x (g x)
bind f g x = f (g x) x
好像g x
的结果构成了这个之前的值。但是,我最近偶然发现了以下示例:
mainA = (\x y z -> (x,y,z)) <$> (2/) <*> (+1) <*> (\x -> x * x)
mainM = (\x -> (\y -> (\z w -> if w == 0 then (0,y,z) else (x,y,z)) =<< (\x -> x * x)) =<< (+1)) =<< (2/)
mainA 0 -- (Infinity,1.0,0.0)
mainM 0 -- (0.0,1.0,0.0)
由于 monadic 操作必须 return monad 我们在 mainM
的定义中有这个额外的 lambda w -> ...
。这使我们能够访问传递给整体计算的初始值。给定 bind f g x = f (g x) x
,这是否意味着函数实例的先前值是 x
而不是 g x
的结果?
抱歉造成混淆!
在这种单行拼写中,mainM
的定义有点难以理解,所以我先添加一些缩进:
mainM =
(\x ->
(\y ->
(\z w -> if w == 0 then (0,y,z) else (x,y,z))
=<< (\x -> x * x))
=<< (+1))
=<< (2/)
Since a monadic action has to return a monad we have this additional lambda w -> ...
within mainM
's definition. This gives us access to the initial value that is passed to the overall computation.
非常正确。如果我们不将该函数编写为两个参数的函数(两个参数的函数是 returns 一个函数的一个参数的函数),可能会更容易看出发生了什么:
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)
应用于初始 parameter/environment 的结果绑定到 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
和 z
)来决定下一步要执行的计算(即使用环境的哪个功能)。我们实际上可以仅使用应用程序重写它:
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
不过,function/reader monad 并不能很好地说明 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
我正在为 monad 的函数实例苦苦挣扎(谁不会?)。 Monads 可能取决于先前的 monadic 计算的值。这对函数 monad 到底意味着什么?
当我们查看 applicative/monad 个实例的基本实现时
ap f g x = f x (g x)
bind f g x = f (g x) x
好像g x
的结果构成了这个之前的值。但是,我最近偶然发现了以下示例:
mainA = (\x y z -> (x,y,z)) <$> (2/) <*> (+1) <*> (\x -> x * x)
mainM = (\x -> (\y -> (\z w -> if w == 0 then (0,y,z) else (x,y,z)) =<< (\x -> x * x)) =<< (+1)) =<< (2/)
mainA 0 -- (Infinity,1.0,0.0)
mainM 0 -- (0.0,1.0,0.0)
由于 monadic 操作必须 return monad 我们在 mainM
的定义中有这个额外的 lambda w -> ...
。这使我们能够访问传递给整体计算的初始值。给定 bind f g x = f (g x) x
,这是否意味着函数实例的先前值是 x
而不是 g x
的结果?
抱歉造成混淆!
在这种单行拼写中,mainM
的定义有点难以理解,所以我先添加一些缩进:
mainM =
(\x ->
(\y ->
(\z w -> if w == 0 then (0,y,z) else (x,y,z))
=<< (\x -> x * x))
=<< (+1))
=<< (2/)
Since a monadic action has to return a monad we have this additional lambda
w -> ...
withinmainM
's definition. This gives us access to the initial value that is passed to the overall computation.
非常正确。如果我们不将该函数编写为两个参数的函数(两个参数的函数是 returns 一个函数的一个参数的函数),可能会更容易看出发生了什么:
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)
应用于初始 parameter/environment 的结果绑定到 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
和 z
)来决定下一步要执行的计算(即使用环境的哪个功能)。我们实际上可以仅使用应用程序重写它:
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
不过,function/reader monad 并不能很好地说明 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