理解简单 Reader monad 的符号:a <- (*2), b <- (+10), return (a+b)

Gue*_*OCs 3 monads haskell do-notation

instance Monad ((->) r) where  
    return x = \_ -> x  
    h >>= f = \w -> f (h w) w  

import Control.Monad.Instances  

addStuff :: Int -> Int  
addStuff = do  
    a <- (*2)  
    b <- (+10)  
    return (a+b)  
Run Code Online (Sandbox Code Playgroud)

我试图通过展开 do 符号来理解这个 monad,因为我认为 do 符号隐藏了发生的事情。

如果我理解正确,会发生以下情况:

(*2) >>= (\a -> (+10) >>= (\b -> return (a+b))) 
Run Code Online (Sandbox Code Playgroud)

现在,如果我们采用 for 的规则>>=,我们必须理解(*2)ash(\a -> (+10) >>= (\b -> return (a+b)))as f。应用hw很容易,让我们只说这是2w(我不知道,如果2w是在Haskell有效,但只是推理可以继续保持这样。现在,我们必须申请fh w2w好了,f只是简单地返回(+10) >>= (\b -> return (a+b))一个特定的a,这是2w在我们的情况f (hw)也是如此(+10) >>= (\b -> return (2w+b))(+10) >>= (\b -> return (2w + b))在最终将其应用于 之前,我们必须首先了解会发生什么w

现在我们(+10) >>= (\b -> return (2w + b))用我们的规则重新识别,his+10fis (\b -> return (2w + b))。让我们先做h w。我们得到w + 10。现在,我们需要申请fh w。我们得到(return (2w + w + 10))

(return (2w + w + 10))也是我们w在第一次>>=尝试 uwind 时需要应用的内容。但我完全迷失了,我不知道发生了什么。

我的想法是否正确?这太令人困惑了。有没有更好的思考方式?

Fyo*_*kin 6

您忘记了运算符>>=不只返回f (h w) w,而是返回\w -> f (h w) w. 也就是说,它返回一个函数,而不是一个数字。

通过错误地替换它,您丢失了最外层的 parameter w,因此难怪它在您的最终表达式中仍然是自由的。

为了正确地做到这一点,你必须完全用函数体代替它们的调用,而不是丢掉东西。

如果你替换最外层>>=,你会得到:

(*2) >>= (\a -> ...) 
==
\w -> (\a -> ...) (w*2) w
Run Code Online (Sandbox Code Playgroud)

然后,如果你替换最里面的>>=,你会得到:

\a -> (+10) >>= (\b -> return (a+b))
==
\a -> \w1 -> (\b -> return (a+b)) (w1 + 10) w1
Run Code Online (Sandbox Code Playgroud)

请注意,我使用w1代替w. 这是为了避免稍后在我组合替换时发生名称冲突,因为这两个ws 来自两个不同的 lambda 抽象,因此它们是不同的变量。

最后,替换为return

return (a+b)
==
\_ -> a+b
Run Code Online (Sandbox Code Playgroud)

现在将最后一个替换插入前一个:

\a -> (+10) >>= (\b -> return (a+b))
==
\a -> \w1 -> (\b -> return (a+b)) (w1 + 10) w1
==
\a -> \w1 -> (\b -> \_ -> a+b) (w1 + 10) w1
Run Code Online (Sandbox Code Playgroud)

最后将其插入到第一个替换中:

(*2) >>= (\a -> ...) 
==
\w -> (\a -> ...) (w*2) w
==
\w -> (\a -> \w1 -> (\b -> \_ -> a+b) (w1 + 10) w1) (w*2) w
Run Code Online (Sandbox Code Playgroud)

现在所有换人都在竞争,我们可以减少。从应用最里面的 lambda 开始\b -> ...

\w -> (\a -> \w1 -> (\_ -> a+w1+10) w1) (w*2) w
Run Code Online (Sandbox Code Playgroud)

现在应用新的最里面的 lambda \_ -> ...

\w -> (\a -> \w1 -> a+w1+10) (w*2) w
Run Code Online (Sandbox Code Playgroud)

现在申请\a -> ...

\w -> (\w1 -> w*2+w1+10) w
Run Code Online (Sandbox Code Playgroud)

最后应用唯一剩余的 lambda \w1 -> ...

\w -> w*2+w+10
Run Code Online (Sandbox Code Playgroud)

瞧!整个函数简化为\w -> (w*2) + (w+10),完全符合预期。