Haskell:列表monad中的变量范围

Jak*_*ake 5 monads haskell list

我正在阅读关于列表monad的在线Haskell书.在本书中,列表monad的定义如下:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)  
    fail _ = []
Run Code Online (Sandbox Code Playgroud)

然后有一个列表monad用法的例子,如下所示:

Prelude> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
Run Code Online (Sandbox Code Playgroud)

我是Haskell的新手,关于这个例子的问题是,为什么变量'n'在lambda表达式中可用return (n,ch).n在另一个lambda表达式中定义,我不明白为什么在一个lambda表达式中定义的变量在后续的lambda表达式中可用.我尝试根据列表monad定义转换下面的示例:

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

<interactive>:32:29: error: Variable not in scope: n
Run Code Online (Sandbox Code Playgroud)

但正如您所看到的,我得到一个错误,说该变量n在另一个lambda表达式的范围内不可用.也许这本书只提出了列表monad定义的简化版本?

chi*_*chi 13

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)  
Run Code Online (Sandbox Code Playgroud)

未被解析为

[1,2] >>= (\n -> ['a','b']) >>= (\ch -> return (n,ch))
Run Code Online (Sandbox Code Playgroud)

但作为

[1,2] >>= \n -> (['a','b'] >>= (\ch -> return (n,ch)))
Run Code Online (Sandbox Code Playgroud)

您的翻译concat / map反映了"错误"解析.我们可以将其改编为正确的.

第一个>>=成为

concat (map (\n -> ???) [1,2])
Run Code Online (Sandbox Code Playgroud)

现在我们可以根据需要翻译内部>>=替换???:

??? = concat (map (\ch -> ???2) ['a','b'])
???2= return (n,ch)
Run Code Online (Sandbox Code Playgroud)

结果:

concat (map (\n -> concat (map (\ch -> return (n,ch)) ['a','b'])) [1,2])
Run Code Online (Sandbox Code Playgroud)


Wil*_*sem 7

因为你的表达:

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
Run Code Online (Sandbox Code Playgroud)

相当于:

--                  body of the lambda expression
--                _________^_______________________
--               /                                 \
[1,2] >>= (\n -> (['a','b'] >>= \ch -> return (n,ch)))
--        \________________ _________________________/
--                         v
--              entire right operand
Run Code Online (Sandbox Code Playgroud)

所以在第一个的右边>>=你写了一个lambda表达式.

[]monad 的定义是:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)
Run Code Online (Sandbox Code Playgroud)

所以你写了:

[1,2] >>= (\n -> (['a','b'] >>= \ch -> return (n,ch)))
 -> concat (map (\n -> (['a','b'] >>= \ch -> return (n,ch)) [1,2])
 -> concat (map (\n -> concat (map (\ch -> return (n,ch)) ['a','b'])) [1,2])
 -> concat (map (\n -> concat (map (\ch -> [(n,ch)]) ['a','b'])) [1,2])
Run Code Online (Sandbox Code Playgroud)

所以n\ch -> [(n,ch)]表达式的范围内.在最后一个声明中,我们获得:

Prelude> concat (map (\n -> concat (map (\ch -> [(n,ch)]) ['a','b'])) [1,2])
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
Run Code Online (Sandbox Code Playgroud)

  • `>> =`的固定性是无关紧要的,列表和monad也是如此.真正的一点是,lambda表达式的主体总是尽可能向右延伸. (4认同)