我来自python世界,但尝试尽可能地使用功能,并改变我的命令性思维。
现在我研究haskell并发现
list = [(x,y) | x<-[1,2,3], y<-[4,5,6]]
被翻译为
list' =
[1,2,3] >>= \x ->
[4,5,6] >>= \y ->
return (x,y)
我尝试逐步理解list monad绑定链的处理:
绑定defind as:
xs >>= f = concat (map f xs)
并且bind是左关联的
所以当我强调,开始时首先使用结果[4,5,6,4执行绑定([1,2,3]&gt;&gt; = \ x - &gt; [4,5,6]) 5,6,4,5,6]
然后接下来绑定 [4,5,6,4,5,6,4,5,6]&gt;&gt; = \ y - &gt; return(x,y)执行
但如果它已经计算好了,它怎么能看到lambda里面的x? x只是根本没有具体值的参数(lambda可以在任何时候使用varios参数调用,我们如何将其置于外部并修复?)。如果它以某种方式可以看到它怎么能知道x呼叫历史是用1,2,3改变的?在我的理解中,第一次绑定计算已经完成,只有结果[4,5,6,4,5,6,4,5,6]可用,接下来是下一个绑定的第一个参数。
所以我无法理解如何阅读这种结构,以及它如何逐步产生正确的结果?
答案 0 :(得分:6)
这是混淆的常见原因。 lambdas的范围尽可能扩展 ,所以这个:
[1,2,3] >>= \x ->
[4,5,6] >>= \y ->
return (x,y)
等同于:
[1,2,3] >>= (\x ->
[4,5,6] >>= (\y ->
return (x,y)))
因此内部lambda \y -> …
位于外部lambda \x -> …
的范围内,意味着x
和y
都在范围内。然后,您可以为>>=
monad实例内联return
和[]
的定义,并逐步完成评估:
concatMap (\x ->
concatMap (\y ->
[(x,y)]) [4,5,6]) [1,2,3]
concat
[ concatMap (\y -> [(1,y)]) [4,5,6]
, concatMap (\y -> [(2,y)]) [4,5,6]
, concatMap (\y -> [(3,y)]) [4,5,6]
]
concat
[ concat [[(1,4)], [(1,5)], [(1,6)]]
, concat [[(2,4)], [(2,5)], [(2,6)]]
, concat [[(3,4)], [(3,5)], [(3,6)]]
]
concat
[ [(1,4), (1,5), (1,6)]
, [(2,4), (2,5), (2,6)]
, [(3,4), (3,5), (3,6)]
]
[ (1,4), (1,5), (1,6)
, (2,4), (2,5), (2,6)
, (3,4), (3,5), (3,6)
]
使用Applicative
(Monad
)和{{1},使用<$>
实例代替fmap
实例,这是一种常见且更简洁的表达方式。 (<*>
)运算符或ap
:
liftA2