函数上下文中 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 的结果绑定到 xyz,分别用它们三个来得到最终的结果。如果我们想要对环境始终如一地使用 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 实际上并未使用先前计算的结果(即 xyz)来决定下一步要执行的计算(即使用环境的哪个功能)。我们实际上可以仅使用应用程序重写它:

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 并不能很好地说明 ApplicativeMonad 之间的差异,因为 。例如,使用 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