Reputation: 331
I've defined my tree as follows:
data Tree a
= Tip
| Node (Tree a) a (Tree a)
deriving Show
and made it an instance of a Functor:
instance Functor Tree where
fmap f Tip = Tip
fmap f (Node leftsub x rightsub) = Node (fmap f leftsub) (f x) (fmap f rightsub)
now I want to define the following function to fold the tree:
foldTree :: b -> (b -> a -> b -> b) -> Tree a -> b
I've tried something like this but I don't think it works since Tree isn't a monoid:
foldTree f z Tip = Tip
foldTree f z (Node leftsub x rightsub) = foldr f leftsub ++ f x ++ foldr f rightsub
I'm having some trouble thinking how to do it. Any help would be appreciated.
Upvotes: 2
Views: 263
Reputation: 120711
First note that foldTree
has three arguments:
foldTree :: b -- ^ Starting value
-> (b -> a -> b -> b) -- ^ Folding function
-> Tree a -- ^ Tree to fold over
-> b
Actually, convention with folds is to put the function argument first, so I'll in the following discuss foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b
.
Now your attempt
foldTree f Tip = Tip
is lacking an argument, and the result type also doesn't make sense: Tip
is a tree(-leaf), but you want the result to be simply b
.
A good way to write functions where you have difficulty seeing what's supposed to happen is to let the compiler help you. Again, foldTree
has three arguments, so in general its definition should have the form
foldTree q r t = _
where t
is the tree, so the first clause will be
foldTree q r Tip = _
Well, you can actually present GHC (>= 7.8) with this “definition”. This is what it replies:
Found hole ‘_’ with type: b
Where: ‘b’ is a rigid type variable bound by
the type signature for
foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b
at /tmp/wtmpf-file26763.hs:6:13
Relevant bindings include
r :: b (bound at /tmp/wtmpf-file26763.hs:8:12)
q :: b -> a -> b -> b (bound at /tmp/wtmpf-file26763.hs:8:10)
foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b
(bound at /tmp/wtmpf-file26763.hs:8:1)
In the expression: _
In an equation for ‘foldTree’: foldTree q r Tip = _
So, it wants a result of type b
. It so happens that you have a value of type b
at hand, namely r
. So one definition for this clause is
foldTree q r Tip = r
In fact that's the correct defintion and the only possible, since the only other thing you have at that point is the function q
, but that would require an a
value to evaluate, of which you have none.
So, let's move on:
foldTree _ r Tip = r
foldTree q r (Node lsub x rsub) = _
gives
Found hole ‘_’ with type: b
Where: ‘b’ is a rigid type variable bound by
the type signature for
foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b
at /tmp/wtmpf-file26763.hs:6:13
Relevant bindings include
rsub :: Tree a (bound at /tmp/wtmpf-file26763.hs:9:27)
x :: a (bound at /tmp/wtmpf-file26763.hs:9:25)
lsub :: Tree a (bound at /tmp/wtmpf-file26763.hs:9:20)
r :: b (bound at /tmp/wtmpf-file26763.hs:9:12)
q :: b -> a -> b -> b (bound at /tmp/wtmpf-file26763.hs:9:10)
foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b
(bound at /tmp/wtmpf-file26763.hs:8:1)
In the expression: _
In an equation for ‘foldTree’: foldTree q r (Node lsub x rsub) = _
Ok, so again you need a b
. You could of course yet again simply return r
, but that would mean the function simply ignores the entire tree. In particular, now you have a value x :: a
which you could pass to q
– sounds like a good idea. The result of q
actually has type b
, so let's try to use that as the result of the entire clause.
q
has three arguments, I'm lazy so I'll again leave typed holes first...
foldTree q r (Node lsub x rsub) = q _q₀ _q₁ _q₂
giving
Found hole ‘_q₀’ with type: b
...
Found hole ‘_q₁’ with type: a
...
Found hole ‘_q₂’ with type: b
...
Relevant bindings include
rsub :: Tree a (bound at /tmp/wtmpf-file26763.hs:9:27)
x :: a (bound at /tmp/wtmpf-file26763.hs:9:25)
lsub :: Tree a (bound at /tmp/wtmpf-file26763.hs:9:20)
r :: b (bound at /tmp/wtmpf-file26763.hs:9:12)
q :: b -> a -> b -> b (bound at /tmp/wtmpf-file26763.hs:9:10)
foldTree :: (b -> a -> b -> b) -> b -> Tree a -> b
(bound at /tmp/wtmpf-file26763.hs:8:1)
Ok, one of these is clear-cut: we have a value x :: a
, so that's the natural fit for _q₁
.
foldTree q r (Node lsub x rsub) = q _q₀ x _q₂
For _q₀
and _q₂
we need once more values of type b
. We could in principle pass r
to both of these, but that would mean we still don't use the subtrees lsub
and rsub
. How could we use those? Well, there's one function in the hint that eats trees: foldTree
. Indeed it also yields b
as the result. So looks like we need recursive calls here. Again use holes for the missing arguments:
foldTree q r (Node lsub x rsub)
= q (foldTree _₀ _₁ lsub) x (foldTree _₂ _₃ rsub)
...
Found hole ‘_₀’ with type: b -> a -> b -> b
...
Relevant bindings include
q :: b -> a -> b -> b (bound at /tmp/wtmpf-file26763.hs:9:10)
aha, so that leads us to
foldTree q r (Node lsub x rsub)
= q (foldTree q _₁ lsub) x (foldTree _₂ _₃ rsub)
...
Found hole ‘_₁’ with type: b
Ok, now we've used everything else, so we might as well just plug in the initial value.
foldTree q r (Node lsub x rsub)
= q (foldTree q r lsub) x (foldTree _₂ _₃ rsub)
and so on, just fill the gaps with what GHC offers you with the right type.
Upvotes: 8