There seems to be a difference in the evaluation order of applicative do notation / ado
vs. applicative lifting via <$>
/map
on the first argument, and <*>
/apply
for remaining arguments.
At least, this is what I have read so far and what is reflected in the course of the exercise shown below. Questions:
- Why is the evaluation order of solution 1 and 2 different (general concepts)?
- How can I rewrite solution 2 (without
ado
), respecting the preorder assertion from the test?
Given
Exercise from the PureScript by Example book (Chapter 7) can be found here:
3.(Medium) Write a function
traversePreOrder :: forall a m b. Applicative m => (a -> m b) -> Tree a -> m (Tree b)
that performs a pre-order traversal of the tree. [...] Applicative do notation (ado) is the easiest way to write this function.
Algebraic data type Tree
:
data Tree a
= Leaf
| Branch (Tree a) a (Tree a)
Test expecting the traverse order [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))
Note: I am not sure, what tell
and runWriter
exactly do - this is a copied code block from the exercise.
For illustration - the example tree looks like this:
What I tried
Solution 1: ado
(works)
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
Solution 2: conventional lifting (does not work)
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
This triggers the error:
expected [1,2,3,4,5,6,7], got [3,2,4,1,6,5,7]