了解 haskell monad 的作用域

Understanding scoping with haskell monads

我正在尝试了解作用域在 do 块中的工作原理。

如果我有以下代码:

l = [1, 2, 3]
m = [1, 2]

那么这很好

res = do
    a <- l
    b <- m
    return (a, b)

和returnml的笛卡尔积。

为了理解范围,我尝试以不同的形式重写它(没有 do 块)

我知道 do 块只是 monadic 操作的语法糖所以我尝试 "unsugar" 它并使用 this 并想出了这个:

res = l >>= (\a -> m) >>= (\b -> return (a, b))

奇怪的是我得到了这个错误 Not in scope: ‘a’

谁能告诉我哪里做错了,可能还有范围界定是如何工作的,因为 do 块中的 return 能够访问一个 ?

看起来真的很神奇

非常感谢

问题是您代码中的 lambda 范围不太正确。它应该一直延伸到表达式的末尾,而不仅仅是围绕小计算。你的代码应该脱糖到

 l >>= (\a -> m >>= (\b -> return (a, b))

顺便把括号去掉,这样会更舒服一些。

 l >>= \a -> m >>= \b -> return (a, b)

但是这样就把意思弄糊涂了。如果你想痛苦地明确,我们可以转换为前缀表示法并说

 bind a f = a >>= f
 bind l (\a -> bind m (\b -> return (a, b))

也许去除一些运算符糖会有所帮助。

请注意 >>= 是如何嵌套在 内部 lambda 中的,而不是围绕它。这确保 a 保留在范围内。事实上,这种嵌套用手写出来有点痛苦,这是 do-notation 的一部分推动力:)

问题出在你的括号上。你写

res = l >>= (\a -> m) >>= (\b -> return (a, b))

但你需要的是

res = l >>= (\a -> m >>= (\b -> return (a, b)))

也可以这样写

res = l >>= \a -> m >>= \b -> return (a, b)

您过早结束了 lambda 表达式绑定 a,然后尝试使用 a