Suppose I have this Tree
type:
{-# LANGUAGE DeriveFoldable, DeriveFunctor #-}
data Tree a = Leaf | Branch (Tree a) a (Tree a) deriving(Functor,Foldable)
instance Traversable Tree where -- equivalent to the one I could derive, but written out for clarity
traverse _ Leaf = pure Leaf
traverse f (Branch l x r) = Branch <$> traverse f l <*> f x <*> traverse f r
It's easy to write a function to calculate the maximum depth of things inside that specific type:
depth Leaf = 0
depth (Branch l _ r) = 1 + max (depth l) (depth r)
But it's not so easy to calculate the maximum depth of things inside an arbitrary Traversable
. I already know that just Functor
isn't enough for this, since you get no information about the "position" of things inside of them via fmap
, and I also already know that just Foldable
isn't enough for this, since foldr
and foldMap
both only give as much structure as lists have. Traversable
might be, though, because it's more general than both Functor
and Foldable
.
However, after doing some experimentation, I don't think there's a way to do it with Traversable
either. Here's my logic so far. Consider these two trees:
fooTree = Branch (Branch Leaf () Leaf) () (Branch Leaf () Leaf)
barTree = Branch (Branch Leaf () (Branch Leaf () Leaf)) () Leaf
Now, traverse (\() -> thingy) fooTree
is:
Branch <$> (Branch <$> pure Leaf <*> thingy <*> pure Leaf) <*> thingy <*> (Branch <$> pure Leaf <*> thingy <*> pure Leaf)
After lots of use of the Applicative laws and some simplification, that becomes:
(\x y z -> Branch (Branch Leaf x Leaf) y (Branch Leaf z Leaf)) <$> thingy <*> thingy <*> thingy
Similarly, traverse (\() -> thingy) barTree
is:
Branch <$> (Branch <$> pure Leaf <*> thingy <*> (Branch <$> pure Leaf <*> thingy <*> pure Leaf)) <*> thingy <*> pure Leaf
After lots of use of the Applicative laws and some simplification, that becomes:
(\x y z -> Branch (Branch Leaf x (Branch Leaf y Leaf)) z Leaf) <$> thingy <*> thingy <*> thingy
Now traverse (\() -> thingy) fooTree
and traverse (\() -> thingy) barTree
look like they have the same "shape" (the only difference is the lambda at the beginning, and even the types of those are the same), but they came from trees that have different depths. This leads me to believe that it's impossible to find the depth in terms of traverse
, but I'm not 100% sure about it and I'm not sure how to explain it rigorously.
Am I right that it's impossible? If so, then how can this actually be explained rigorously? If not, then how would you implement it?