I'm working through the wonderful Haskell Book. At the end of the Traversable chapter (21), I need to write an instance for the following Tree:
data Tree a =
Empty
| Leaf a
| Node (Tree a) a (Tree a)
Here is a link to the full code of my solution. The exercises recommends trying to implement both foldMap
and foldr
. This is how I implemented foldr
(without putting much thought into the invocation order):
foldr _ z Empty = z
foldr f z (Leaf x) = f x z
foldr f z (Node left x right) =
f x $ foldr f (foldr f z left) right
I then implemented foldMap
as follows:
foldMap f Empty = mempty
foldMap f (Leaf x) = f x
foldMap f (Node left x right) =
foldMap f left <> f x <> foldMap f right
When I run QuickCheck's foldable
test batch, I get some failures. Changing my foldr
implementation to the following makes all the tests pass:
foldr _ z Empty = z
foldr f z (Leaf x) = f x z
foldr f z (Node left x right) =
foldr f (f x (foldr f z right)) left
I tried running the failing test case on my own, but couldn't recreate the failure:
*Ch21_12_ExercisesTree Data.Monoid> tree = Node (Node (Leaf (-5)) 3 (Node (Leaf 3) 5 Empty)) (-2) Empty
*Ch21_12_ExercisesTree Data.Monoid> foldr (<>) (mempty :: Sum Int) t
Sum {getSum = 4}
*Ch21_12_ExercisesTree Data.Monoid> foldMap Sum t
Sum {getSum = 4}
I suspect there's something I'm not figuring out about the fold
ing function that QuickCheck is using.
Questions:
- Why are failures occurring?
- Is there a way to get the function used in the test by QuickCheck?
I just realized that I used a commutative Monoid ... I was able to recreate the failure using a non-commutative Monoid:
This is probably a simple case. I'd imagine that in production code with real data types this could be much more complicated.