为什么 >> 在 haskell 中复制右侧操作数

Why >> duplicates right-hand side operand in haskell

我搜索了一个字符串重复 n 次的解决方案。我从这个solution中找到了duplicate n str = [1..n] >> str。 我想知道为什么这个方法会重复 str.

我搜索了 >>,发现了以下几点:

k >> f = k >>= \_ -> f

a >> b >> c >> d
-- is is equivalent to
do a
   b
   c
   d

那我试试这个

ghci> do [1..3]; "a"
"aaa"

但我仍然不明白它是如何工作的。谁能解释这种行为?

列表>>=的定义是concatMap:对列表的每个元素应用一个函数,然后连接结果。当您使用 >> 时,这意味着应用一个函数 忽略 列表的每个元素,而不是 returns right-hand 侧的常量值共 >>

[1..3] >> "a" == [1..3] >>= \_ -> "a"
              == concatMap (\_ -> "a") [1..3]
              == "a" ++ "a" ++ "a"
              == "aaa"

根据 Monad 法则,

a >> b >> c >> d
-- is equivalent to
--                                        (values)   (computations)
do a             do {      a            do {  _x   <-     a
   b                ;      b               ;  _y   <-     b
   c                ;      c               ;  _z   <-     c
   d                ; r <- d               ;   r   <-     d
                    ; return r             ;   return r
                    }                      }

并用 Monad Comprehensions 编写它——对于列表,non-coincidentally,与 List Comprehensions 完全相同——它变成了

    [ r | _x <- a,  _y <- b,  _z <- c,  r <- d ]

这意味着,在伪代码中,

for each _x in a:             for each _x in a:
  for each _y in b:             for each _y in b:
    for each _z in c:             for each _z in c:
      for each r in d:              do  d
         do  yield r

所以我们只是“做”相同的 d,一遍又一遍,对于每个 其上方操作的结果 组合 嵌套时尚.

对于列表,这意味着将 d 的元素重复拼接到结果列表中。

在某种意义上广义(嵌套)循环是(单子)/applicative/functors ,描述了从最深层次产生最终结果的组合计算同样的:(*)

  Functor                Applicative                  Monad
    
for x in a:             for x in a                for x in a:
  do  yield (foo x)     and y in b:                 for y in (bar x):
                          do  yield (foo x y)         do  yield (foo x y)

  "loop"                  "loops"                    "nested loops"
                                                   (created on the fly)

(*) 无论有多少层,从整体上看,“循环”组合一个接一个地产生结果,正如每个“循环”产生其枚举在分离,一一。它是如此微不足道,我们甚至不会在命令式循环中注意到它。

每种特定类型的 Functor 都赋予上面的“for”、“in”和“do yield”含义。对于列表,它意味着拼接结果,通过 concat/concatMap.

实现