了解haskell monads的范围

DAR*_*UCK 4 monads haskell scoping

我试图理解范围在do块中是如何工作的.

如果我有以下代码:

l = [1, 2, 3]
m = [1, 2]
Run Code Online (Sandbox Code Playgroud)

然后这工作正常

res = do
    a <- l
    b <- m
    return (a, b)
Run Code Online (Sandbox Code Playgroud)

并返回笛卡尔乘积ml.

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

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

res = l >>= (\a -> m) >>= (\b -> return (a, b))
Run Code Online (Sandbox Code Playgroud)

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

任何人都可以告诉我我做错了什么,可能,范围如何工作,因为它真的看起来像魔术return块在do块中能够访问?

非常感谢你

Dan*_*zer 12

问题是代码中lambda的范围不太对.它应该一直延伸到表达式的末尾,而不仅仅是小计算.你的代码应该是desugar

 l >>= (\a -> m >>= (\b -> return (a, b))
Run Code Online (Sandbox Code Playgroud)

你可以顺便放下括号,这样可以让它更愉快.

 l >>= \a -> m >>= \b -> return (a, b)
Run Code Online (Sandbox Code Playgroud)

但这种模糊了意义.如果你想要明确,我们可以转换为前缀表示法并说

 bind a f = a >>= f
 bind l (\a -> bind m (\b -> return (a, b))
Run Code Online (Sandbox Code Playgroud)

也许剥离一些操作员的糖有帮助.

注意>> ='嵌套 lambda 内部,而不是它周围.这可确保a保留在范围内.事实上,这种嵌套是手工写出来的一点点痛苦,这是写法的推动力的一部分:)


dfe*_*uer 7

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

res = l >>= (\a -> m) >>= (\b -> return (a, b))
Run Code Online (Sandbox Code Playgroud)

但你需要的是

res = l >>= (\a -> m >>= (\b -> return (a, b)))
Run Code Online (Sandbox Code Playgroud)

也可以写

res = l >>= \a -> m >>= \b -> return (a, b)
Run Code Online (Sandbox Code Playgroud)

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