我是一名 C++/Rust 程序员,出于好奇,我目前正在学习 Haskell。正如所料,我在尝试理解 Monad 时遇到了一些障碍:
我已经在 Wikibook 上阅读了 Monad 的定义,包括它们与范畴论中的数学定义和三个 Monad 定律的关系。我也知道
do
符号是 Monad 的语法糖,但 do
块中的某些行为仍然让我困惑。
在一些教程中,我注意到
bind
(>>=
) 通常被解释为 将第二个参数(函数)应用于第一个参数中包含的值并返回应用函数的结果。然而,从 Monad 法则的角度来看,这似乎并不是唯一可能的实现方式。我不确定 bind
是否必须始终遵循此模式,或者它是否只是一个 common 实现。
如果这种模式确实很常见,那么我必须考虑其他实现。最让我困惑的是我们知道:
do { v <- y; ... } = y >>= (\v -> ...)
所以这里
<-
的含义应该和>>=
的实现有直接关系。但如果不是上面提到的常见实现,那么 <-
可能不再带有声明或绑定的语义。在这种情况下我们该如何理解呢?
不幸的是,作为一个初学者,尽管我付出了努力,但我还没有想到一个不常见但合法实施
bind
的例子。
以下是我的问题摘要:
bind
(>>=
) 实现?bind
的不常见实现下,如何理解<-
符号中do
的含义?也许最简单的是list monad,其实现为:
instance Monad [] where
return x = [x]
x >>= f = [y | xi <- x, y <- f xi]
这里
x >>= f
本质上与 concatMap f x
相同,因此对于列表 xi
中的每个元素 x
调用该元素上的 f
,并将 f xi
生成的项目列表添加到结果中.
这意味着如果我们写:
foo :: [Int]
foo = do
xi <- [1, 4, 2, 5]
[xi, xi + 1]
翻译为:
[1, 4, 2, 5] >>= \xi -> [xi, xi + 1]
因此,对于列表中的每个项目
[1,4,2,5]
,产量x
和x+1
,所以:
ghci> foo
[1,2,4,5,2,3,5,6]
这里
那么
可能不再带有 C 风格赋值或绑定的语义。<-
它并不真正具有 C 风格赋值的语义,即使使用
IO
也不行。在 Haskell 中,您不会为变量“赋值”。您声明一个变量,并且该变量始终保持相同的值。因此,您不能创建循环,例如“操纵”变量的值。不变性是声明性语言的核心特征之一。