applicative do notation / ado
vs. applicative lift via <$>
/ map
on the first argument 和<*>
/apply
对于其余论点的评估顺序似乎有所不同。
至少,这是我到目前为止所读到的内容,并且反映在下面所示的练习过程中。问题:
- 为什么解决方案1和2的评估顺序不同(一般概念)?
- 如何重写解决方案 2(没有
ado
),尊重测试中的预购断言?
给定
PureScript by Example 书(第 7 章)中的练习可以在这里找到:
3.(中)编写一个
traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b)
执行树的前序遍历的函数。[...] Applicative do notation (ado) 是编写此函数的最简单方法。
代数数据类型Tree
:
data Tree a
= Leaf
| Branch (Tree a) a (Tree a)
测试期望遍历顺序[1,2,3,4,5,6,7]
:
Assert.equal (1 .. 7)
$ snd
$ runWriter
$ traversePreOrder (\x -> tell [ x ])
$ Branch (Branch (leaf 3) 2 (leaf 4)) 1 (Branch (leaf 6) 5 (leaf 7))
注意:我不确定,具体是做什么tell
的runWriter
- 这是从练习中复制的代码块。
为了说明 - 示例树如下所示:
我试过的
解决方案1:(ado
有效)
traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b)
traversePreOrder f Leaf = pure Leaf
traversePreOrder f (Branch tl v tr) = ado
ev <- f v
etl <- traversePreOrder f tl
etr <- traversePreOrder f tr
in Branch etl ev etr
解决方案2:常规吊装(不起作用)
traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b)
traversePreOrder f Leaf = pure Leaf
traversePreOrder f (Branch tl v tr) =
let
ev = f v -- I consciously tried to place this evaluation first, does not work
etl = traversePreOrder f tl
etr = traversePreOrder f tr
in
Branch <$> etl <*> ev <*> etr
这会触发错误:
预期 [1,2,3,4,5,6,7],得到 [3,2,4,1,6,5,7]