我不了解 Haskell,我只是想通过玩弄它来学习它。我试图理解 io、monad 等,并在解释器(GHCi,版本 7.10.2,WinGHCI)中写下了这个:
Prelude> [1,1] >> "ok"
"okok"
Prelude> [1,1,1] >> "ok"
"okokok"
Prelude> [1..10] >> "ok"
"okokokokokokokokokok"
Prelude> [1] >> "ok" >> [1] >> "ok"
"okok"
Prelude> [1,2] >> "ok" >> [1,2] >> "ok"
"okokokokokokokok"
Prelude> [1..10] >> [1..10]
[1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10]
想解释一下吗?为什么列表中元素的数量会影响写入“ok”的次数(或者在最后一种情况下,会影响写入数组的次数)? >> 运算符不应该这样做,是吗?
最佳答案
首先请注意,这与IO
没有任何关系。它与单子(monad)有关,但与一个非常具体的单子(monad)有关:列表单子(monad)。
instance Monad [] where
return x = [x]
f >>= xs = concat $ map f xs -- aka `(>>=) = concatMap`.
最好了解列表理解,它基本上是语法糖†:
[ result x y z | x <- bla, y <- foo x, z <- bar ]
翻译成
bla >>=
\x -> foo x >>=
\y -> bar >>=
\z -> return (result x y z)
现在,a>>b
只是 a >>=\_ -> b
的捷径,即它忽略了包含在 LHS monadic 中的值行动,但仍然“假装”在 RHS 中使用它们。所以,[1,1,1] >> "ok"
等同于
[ "ok" | _ <- [1,1,1] ]
至少差不多...实际上这与 [1,1,1] >> return "ok"
相同,即 >> ["ok"]
, 这将给
["ok", "ok", "ok"]
如果省略单例 return
,每个 "ok"
将不会包含在列表和那些连接的列表中,而是字符串本身都由 monadic bind 连接.这就是您如何获得 "okokok"
。
†等效的默认实现实际上有点不同,但是使用 -XMonadComprehensions
扩展,列表推导确实以这种方式工作。
关于list ->> 运算符——Haskell 中的穷人循环?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31848124/