>> 运算符 - Haskell 中的穷人循环?

>> operator - poor man's loop in Haskell?

我不知道 Haskell,我只是想用它来学习它。我试图理解 io、monads 等,并在解释器(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 没有任何关系。它与单子有关,但与一个非常具体的单子有关:列表单子。

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 绑定串联。这就是您获得 "okokok".

的方式

等效的默认实现实际上有点不同,但是使用 -XMonadComprehensions 扩展,列表理解确实以这种方式工作。