Haskell:列表monad中的变量范围

时间:2017-07-28 16:01:04

标签: list haskell monads

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

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

然后有一个列表monad用法的例子:

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

我是Haskell的新手,关于这个例子的问题是,变量' n'在return (n,ch)的lambda表达式中可用。 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

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

3 个答案:

答案 0 :(得分:12)

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

未解析为

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

但是作为

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

您对concat / map的翻译反映了错误&#34;解析。我们可以将其改编为正确的。

第一个>>=成为

concat (map (\n -> ???) [1,2])

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

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

结果:

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

答案 1 :(得分:7)

因为你的表达:

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

相当于:

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

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

[] monad的定义是:

instance Monad [] where  
    return x = [x]  
    xs >>= f = concat (map f xs)

所以你写了:

[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])

因此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')]

答案 2 :(得分:0)

@chi和@Willem Van Onsem的答案非常完美,但我想提一下,列表理解只是这个monadic工作的语法糖;

Prelude> [(x,y) | x <- [1,2], y <- ['a', 'b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

然而,因为列表也是Haskell中的applicator仿函数类的实例,所以在不触及monad实例的情况下完成这项工作的另一个好方法是;

Prelude> (,) <$> [1,2] <*> ['a','b']
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

我想详细说明后者,

(,)实际上是一个带有两个参数和一个类型签名的函数; a -> b -> (a,b)。而<$>fmap的内联形式。因此,当我们应用(,) <$> [1,2]时,我们得到的是一个应用函子(函数列表),如[(,) 1, (,) 2]。现在,我们可以应用具有类型签名<*>的应用运算符Applicative f => f (a -> b) -> f a -> f b。在类型签名f中,不应该与函数混淆。它在这里指定了一个恰好是[](列表)类型的仿函数。其中说,<*>将从函子中解包所包含的函数,并将其应用于提供的函子所包含的值,以便以相同的函子类型返回这些应用程序的结果(s) 。很明显,由于列表应用程序是由所有元素一对一定义的,因此结果证明是所有组合的元组列表。