为什么列出monad按顺序组合?

时间:2017-11-20 17:12:57

标签: list haskell syntax monads parentheses

我正在阅读列表monad并遇到:

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  

它产生

[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

以下是我的理解:

隐含的括号是:

([1,2] >>= \n -> ['a','b']) >>= (\ch -> return (n,ch))

([1,2] >>= \n -> ['a','b'])应该[('a',1),('b',1),('a',2),('b',2)]

因为

instance Monad [] where  
  return x = [x]  
  xs >>= f = concat (map f xs)   -- this line
  fail _ = []

所以concat (map f xs)concat (map (\n -> ['a','b']) [1,2]),它应该产生[('a',1),('b',1),('a',2),('b',2)] - 与实际输出完全相反。

然后我不明白>>= (\ch -> return (n,ch))部分 - 我认为n这里没有任何意义。那个具体的推理存在缺陷,请你解释一下这个表达式([1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch))是如何逐步计算出来的?

4 个答案:

答案 0 :(得分:8)

你的隐含括号是错误的。你拥有它的方式,第一个lambda的n参数不在return的范围内。它更像是:

([1,2] >>= (\n -> ['a','b'] >>= (\ch -> return (n,ch))))

哪个成为:

concatMap (\n -> concatMap (\ch -> [(n,ch)]) ['a','b']) [1,2]

答案 1 :(得分:4)

不,你使用了错误的括号。它嵌套在右边,所以

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  
=
[1,2] >>= (\n -> ['a','b'] >>= (\ch -> return (n,ch) ))
=
do { n <- [1,2]
   ; do { ch <- ['a','b']
        ; return (n,ch) }}
=
for n in [1,2]:              -- pseudocode
    for ch in ['a','b']:
        return (n,ch)
=
[ r | n <- [1,2], ch <- ['a','b'], r <- [(n,ch)] ]    -- return == (:[])
=
[ (n,ch) | n <- [1,2], ch <- ['a','b'] ]
=
pure (,) <*> [1,2] <*> ['a','b']      -- using list applicative
=
[(1,'a'), (1,'b'), (2,'a'), (2,'b')]

并且最里面的列表是&#34;旋转&#34;最快的,就像汽车的里程表一样。

你是绝对正确的,如果错误的括号,n绑定没有任何意义。正是由于这个原因,必须关联到右边,才能使嵌套绑定成为可能;对于嵌套计算是monad的本质

[ foo x   | x <- xs ]           -- functor : amenable computations
[ bar x y | x <- xs AND y <- ys ]     -- applicative : combinable computations
[ baz x y | x <- xs, y <- foo x ]         -- monad : reinterpretative computations

(是的,在学习材料中省略括号是所有邪恶的根源......不是真的,但仍然......)

答案 2 :(得分:3)

  

隐含的括号是:

([1,2] >>= \n -> ['a','b']) >>= (\ch -> return (n,ch))

这是错误的。 \具有最低优先级,因此延伸到表达式的末尾。所以括号是:

[1,2] >>= (\n -> ['a','b'] >>= (\ch -> return (n,ch)))
  

([1,2] >>= \n -> ['a','b'])应该[('a',1),('b',1),('a',2),('b',2)]

从上面的括号([1,2] >>= \n -> ['a','b'])可以看出,实际上并不是给定表达式的子表达式。但如果是的话,其结果将是['a', 'b', 'a', 'b']n实际上并未在表达式['a', 'b']中的任何位置使用n,因此数字无法在结果中出现。

  

然后我不理解&gt;&gt; =(\ ch - &gt; return(n,ch))部分 - 我认为这里没有任何意义。

鉴于你的括号n确实在那里未定义。但是如果给出正确的括号,应该清楚\n -> ...来自哪里:我们仍然在n函数内,所以[1, 2]仍然引用该函数的参数,即let q = Promise.resolve(); let i = setInterval(() => { q = q.then(() => { return new Promise((resolve) => { setTimeout(() => { console.log("one resolved"); resolve(); }, 1) }); }); }, 2); 列表的当前元素。

答案 3 :(得分:1)

你对concat (map (\n -> ['a','b']) [1,2])的语义错了。我已经在下面逐步评估了它。

concat (map (\n -> ['a','b']) [1,2])

concat [(\n -> ['a','b']) 1, (\n -> ['a','b']) 2]

concat [['a','b'], ['a','b']]

['a','b','a','b']

最后一个绑定用于实际使其成为元组列表。