A catamorphism can either deconstruct a value
[1,2,3].reduce((acc, x) => acc + x, 0); // 6
or maintain the structure and act like the identity of the underlying type:
[1,2,3].reduce((acc, x) => acc.concat([x]), []); // [1,2,3]
With lists (or arrays in JS) the catamorphism and the fold (of a foldable container) coincide.
However, with trees they do not:
const treeCata = f => ([x, xs]) =>
f(x) (arrMap(treeCata(f)) (xs));
const arrMap = f => xs =>
xs.map((x, i) => f(x, i));
const arrFold = f => acc => xs =>
xs.reduce((acc, x) => f(acc) (x), acc);
const log = x => (console.log(x), x);
const Node = (x, xs) => ([x, xs]);
const Node_ = x => xs => ([x, xs]);
const tree = Node(1, [
Node(2, [
Node(3, []),
Node(4, [])
]),
Node(5, [])
]);
const foo = treeCata(Node_) (tree);
const bar = treeCata(x => xs => x + arrFold(y => z => y + z) (0) (xs)) (tree);
log(foo);
log(bar);
The role as identity works as expected. The deconstruction, however, is a bit more involved. As a matter of fact I need a list fold to conduct it. I can actually see that there is one layer of deconstruction, because otherwise a list fold wouldn't be enough for a non-linear tree. But still having to use another fold seems odd.
I can only guess that this behavior is related to the terse definition of the used catamorphism. It only takes a single case into account the product Tree a = Node a [Tree a]
. Maybe it would be more promising if I'd fully embrace the algebraic structure of the type and distingiush Node
/Array
constructor and the empty array case.
But does this mean treeCata
is not a proper catamorphism? What is it lacking?
I think your
treeCata
is fine, and is a valid catamorphism for the typeTree a = Node a [Tree a]
you defined. The underlying functor isTreeF a b = Node a [b]
, the structure which all your algebras will have to deal with. The catamorphism only handles the recursion for you, not the list structure of your node children. If it did, it would hide information from you, and you would not be able to reconstruct the tree using the initial algebra.Of course, you might want to define a few helper functions on
TreeF
. A particularly interesting one, due to the non-emptiness of your nodes, might beWith this, you can indeed define your sum function as
As @Aadit remarks, this is the function that lets us derive the traversal fold from the structural fold.