[英]Haskell: variable scope in list monad
我正在阅读关于列表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定义的简化版本?
[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
翻译反映了“错误”解析。 我们可以将其改编为正确的。
第一个>>=
成为
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,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')]
那么@chi和@Willem Van Onsem的答案是完美的,但只是为了变化,我想提一下,列表理解只是这个monadic工作的语法糖;
Prelude> [(x,y) | x <- [1,2], y <- ['a', 'b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
然而,因为列表也是Haskell中应用程序类的实例,所以在不触及monad实例的情况下完成这项工作的另一个好方法是;
Prelude> (,) <$> [1,2] <*> ['a','b']
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
我想详细说明后者,
(,)
实际上是一个带有两个参数和一个类型签名的函数; a -> b -> (a,b)
。 而<$>
是fmap
的内联形式。 因此,当我们应用(,) <$> [1,2]
,我们得到的是一个应用函子(函数列表),如[(,) 1, (,) 2]
。 现在我们可以应用具有类型签名的applicative运算符<*>
Applicative f => f (a -> b) -> fa -> fb
在类型签名中f
不应该与函数混淆。 它在这里指定一个恰好是[]
(列表)类型的仿函数。 其中, <*>
将从函子中解包所包含的函数,并将其应用于提供的函子的包含值,以便以相同的函子类型返回这些应用程序的结果。 很明显,由于列表应用程序是由所有元素一对一定义的,因此结果证明是所有组合的元组列表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.