简体   繁体   English

Haskell:列表monad中的变量范围

[英]Haskell: variable scope in list monad

I was reading an online Haskell book about list monad. 我正在阅读关于列表monad的在线Haskell书。 In the book, the list monad was defined like this: 在本书中,列表monad的定义如下:

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

And then there was an example of list monad usage like this: 然后有一个列表monad用法的例子,如下所示:

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

I'm new to Haskell and my question about the example is, how come the variable 'n' is available inside the lambda expression of return (n,ch) . 我是Haskell的新手,关于这个例子的问题是,为什么变量'n'在return (n,ch)的lambda表达式中可用。 n is defined in the other lambda expression and I don't understand why a variable defined in one lambda expression is available in subsequent lambda expressions. n在另一个lambda表达式中定义,我不明白为什么在一个lambda表达式中定义的变量在后续的lambda表达式中可用。 I tried converting the example like below according to the list monad definition: 我尝试根据列表monad定义转换下面的示例:

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

<interactive>:32:29: error: Variable not in scope: n

But as you can see, I got an error saying that the variable n is not available in the scope of the other lambda expression. 但是正如你所看到的,我得到一个错误,说变量n在另一个lambda表达式的范围内不可用。 Maybe the book just presented a simplified version of list monad definition? 也许这本书只提出了列表monad定义的简化版本?

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

is NOT parsed as 未被解析为

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

but as 但作为

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

Your translation with concat / map reflects the "wrong" parsing. 您使用concat / map翻译反映了“错误”解析。 We can adapt that to the correct one. 我们可以将其改编为正确的。

The first >>= becomes 第一个>>=成为

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

and now we can translate the inner >>= replacing ??? 现在我们可以翻译内部>>=替换??? as needed: 如所须:

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

Result: 结果:

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

Because your expression: 因为你的表达:

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

is equivalent to: 相当于:

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

So on the right of the first >>= you have written a lambda expression . 所以在第一个>>=的右边你已经写了一个lambda表达式

The definition of the [] monad is: [] monad的定义是:

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

So you have written: 所以你写了:

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

So n is in the scope of the \\ch -> [(n,ch)] expression. 所以n\\ch -> [(n,ch)]表达式的范围内。 And with the last statement, we obtain: 在最后一个声明中,我们获得:

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

Well @chi and @Willem Van Onsem 's answers are perfectly at place yet just for variety i would like to mention that list comprehensions would just be a syntactical sugar for this monadic job as; 那么@chi和@Willem Van Onsem的答案是完美的,但只是为了变化,我想提一下,列表理解只是这个monadic工作的语法糖;

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

However since lists are also instances of applicative functor class in Haskell another nice way of doing this job without touching monad instance would be; 然而,因为列表也是Haskell中应用程序类的实例,所以在不触及monad实例的情况下完成这项工作的另一个好方法是;

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

I would like to elaborate the latter a little bit, 我想详细说明后者,

(,) is in fact a function which takes two arguments and with a type signature; (,)实际上是一个带有两个参数和一个类型签名的函数; a -> b -> (a,b) . a -> b -> (a,b) Whereas <$> is the inline form of fmap . <$>fmap的内联形式。 So when we apply (,) <$> [1,2] what we get is an applicative functor (a list of functions) like [(,) 1, (,) 2] . 因此,当我们应用(,) <$> [1,2] ,我们得到的是一个应用函子(函数列表),如[(,) 1, (,) 2] Now we can apply the applicative operator <*> which has the type signature Applicative f => f (a -> b) -> fa -> fb In the type signature f shouldn't be confused with function. 现在我们可以应用具有类型签名的applicative运算符<*> Applicative f => f (a -> b) -> fa -> fb在类型签名中f不应该与函数混淆。 It designates a functor which happens to be the [] (list) type here. 它在这里指定一个恰好是[] (列表)类型的仿函数。 Which says, <*> will unwrap the contained function(s) from the functor and apply it to the provided functor's contained value(s) to return the result(s) of these applications in the same functor type. 其中, <*>将从函子中解包所包含的函数,并将其应用于提供的函子的包含值,以便以相同的函子类型返回这些应用程序的结果。 So obviously since list applications are defined by all elements one to one the result turns out to be a list of tuples of all combinations. 很明显,由于列表应用程序是由所有元素一对一定义的,因此结果证明是所有组合的元组列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM