matt
matt

Reputation: 2079

Haskell: Folding over trees

I am going through some old courses online and came across this task:

data Tree a = Leaf 
            | Branch a (Tree a) (Tree a) 
            deriving (Show, Eq)

fold :: (a -> b -> b -> b) -> b -> Tree a -> b
fold _ acc Leaf           = acc
fold f acc (Branch v l r) = f v (fold f acc l) (fold f acc r)

foldRT :: (a -> b -> b) -> b -> Tree a -> b
foldRT _ acc Leaf = acc
foldRT f acc (Branch v l r) = foldRT f (f v (foldRT f acc r)) l

The task is to rewrite foldRT in terms of fold. I have been stuck on it for ages and can't wrap my head around it.

A walk through would be greatly appreciated.

Upvotes: 2

Views: 253

Answers (1)

duplode
duplode

Reputation: 34398

As its name and type signature suggest, foldRT is a bona fide right fold for your tree (you can convince yourself of that by hand-evaluating it for something like Branch 1 (Branch 0 Leaf Leaf) (Branch 2 Leaf Leaf)). That means implementing Foldable will give you foldRT: foldRT = foldr. Why is that relevant? Because, in this case, it is much easier to implement foldMap than it is to figure out how to write foldr directly:

-- Note that l and r here aren't the subtrees, but the results of folding them.
instance Foldable Tree where
    foldMap f = fold (\v l r -> l <> f v <> r) mempty

If you want to write foldRT without relying on a Foldable instance, all you need is expanding the foldMap-based definition of foldr (see the answers to this question for the details I'll gloss over here):

foldRT f z t = appEndo (foldMap (Endo . f) t) z

-- To make things less clumsy, let's use:
foldEndo f = appEndo . foldMap (Endo . f)
-- foldEndo f = flip (foldRT f)

foldEndo f = appEndo . fold (\v l r -> l <> Endo (f v) <> r) mempty

-- As we aren't mentioning foldMap anymore, we can drop the Endo wrappers.
-- mempty @(Endo _) = Endo id
-- Endo g <> Endo f = Endo (g . f)
foldEndo f = fold (\v l r -> l . f v . r) id

-- Bringing it all back home:
foldRT :: (a -> b -> b) -> b -> Tree a -> b
foldRT f z t = fold (\v l r -> l . f v . r) id t z

(Note that we ultimately reached a solution of the sort suggested by Carl, only through an indirect route.)

Upvotes: 1

Related Questions