# Rose tree bifunctor by Mark Seemann

*A rose tree forms a bifunctor. An article for object-oriented developers.*

This article is an instalment in an article series about bifunctors. While the overview article explains that there's essentially two practically useful bifunctors, here's a third one. rose trees.

### Mapping both dimensions #

Like in the previous article on the Either bifunctor, I'll start by implementing the simultaneous two-dimensional translation `SelectBoth`

:

public static IRoseTree<N1, L1> SelectBoth<N, N1, L, L1>( this IRoseTree<N, L> source, Func<N, N1> selectNode, Func<L, L1> selectLeaf) { return source.Cata( node: (n, branches) => new RoseNode<N1, L1>(selectNode(n), branches), leaf: l => (IRoseTree<N1, L1>)new RoseLeaf<N1, L1>(selectLeaf(l))); }

This article uses the previously shown Church-encoded rose tree and its catamorphism `Cata`

.

In the `leaf`

case, the `l`

argument received by the lambda expression is an object of the type `L`

, since the `source`

tree is an `IRoseTree<N, L>`

object; i.e. a tree with leaves of the type `L`

and nodes of the type `N`

. The `selectLeaf`

argument is a function that converts an `L`

object to an `L1`

object. Since `l`

is an `L`

object, you can call `selectLeaf`

with it to produce an `L1`

object. You can use this resulting object to create a new `RoseLeaf<N1, L1>`

. Keep in mind that while the `RoseLeaf`

class requires two type arguments, it never requires an object of its `N`

type argument, which means that you can create an object with any *node* type argument, including `N1`

, even if you don't have an object of that type.

In the `node`

case, the lambda expression receives two objects: `n`

and `branches`

. The `n`

object has the type `N`

, while the `branches`

object has the type `IEnumerable<IRoseTree<N1, L1>>`

. In other words, the `branches`

have already been translated to the desired result type. That's how the catamorphism works. This means that you only have to figure out how to translate the `N`

object `n`

to an `N1`

object. The `selectNode`

function argument can do that, so you can then create a new `RoseNode<N1, L1>`

and return it.

This works as expected:

> var tree = RoseTree.Node("foo", new RoseLeaf<string, int>(42), new RoseLeaf<string, int>(1337)); > tree RoseNode<string, int>("foo", IRoseTree<string, int>[2] { 42, 1337 }) > tree.SelectBoth(s => s.Length, i => i.ToString()) RoseNode<int, string>(3, IRoseTree<int, string>[2] { "42", "1337" })

This *C# Interactive* example shows how to convert a tree with internal string nodes and integer leaves to a tree of internal integer nodes and string leaves. The strings are converted to strings by counting their `Length`

, while the integers are turned into strings using the standard `ToString`

method available on all objects.

### Mapping nodes #

When you have `SelectBoth`

, you can trivially implement the translations for each dimension in isolation. For tuple bifunctors, I called these methods `SelectFirst`

and `SelectSecond`

, while for Either bifunctors, I chose to name them `SelectLeft`

and `SelectRight`

. Continuing the trend of naming the translations after what they translate, instead of their positions, I'll name the corresponding methods here `SelectNode`

and `SelectLeaf`

. In Haskell, the functions associated with `Data.Bifunctor`

are always called `first`

and `second`

, but I see no reason to preserve such abstract naming in C#. In Haskell, these functions are part of the `Bifunctor`

type class; the abstract names serve an actual purpose. This isn't the case in C#, so there's no reason to retain the abstract names. You might as well use names that communicate intent, which is what I've tried to do here.

If you want to map only the internal nodes, you can implement a `SelectNode`

method based on `SelectBoth`

:

public static IRoseTree<N1, L> SelectNode<N, N1, L>( this IRoseTree<N, L> source, Func<N, N1> selector) { return source.SelectBoth(selector, l => l); }

This simply uses the `l => l`

lambda expression as an ad-hoc *identity* function, while passing `selector`

as the `selectNode`

argument to the `SelectBoth`

method.

You can use this to map the above `tree`

to a tree made entirely of numbers:

> var tree = RoseTree.Node("foo", new RoseLeaf<string, int>(42), new RoseLeaf<string, int>(1337)); > tree.SelectNode(s => s.Length) RoseNode<int, int>(3, IRoseTree<int, int>[2] { 42, 1337 })

Such a tree is, incidentally, isomorphic to a 'normal' tree. It might be a good exercise, if you need one, to demonstrate the isormorphism by writing functions that convert a `Tree<T>`

into an `IRoseTree<T, T>`

, and vice versa.

### Mapping leaves #

Similar to `SelectNode`

, you can also trivially implement `SelectLeaf`

:

public static IRoseTree<N, L1> SelectLeaf<N, L, L1>( this IRoseTree<N, L> source, Func<L, L1> selector) { return source.SelectBoth(n => n, selector); }

This is another one-liner calling `SelectBoth`

, with the difference that the identity function `n => n`

is passed as the first argument, instead of as the last. This ensures that only `RoseLeaf`

values are mapped:

> var tree = RoseTree.Node("foo", new RoseLeaf<string, int>(42), new RoseLeaf<string, int>(1337)); > tree.SelectLeaf(i => i % 2 == 0) RoseNode<string, bool>("foo", IRoseTree<string, bool>[2] { true, false })

In the above *C# Interactive* session, the leaves are mapped to Boolean values, indicating whether they're even or odd.

### Identity laws #

Rose trees obey all the bifunctor laws. While it's formal work to prove that this is the case, you can get an intuition for it via examples. Often, I use a property-based testing library like FsCheck or Hedgehog to demonstrate (not prove) that laws hold, but in this article, I'll keep it simple and only cover each law with a parametrised test.

private static T Id<T>(T x) => x; public static IEnumerable<object[]> BifunctorLawsData { get { yield return new[] { new RoseLeaf<int, string>("") }; yield return new[] { new RoseLeaf<int, string>("foo") }; yield return new[] { RoseTree.Node<int, string>(42) }; yield return new[] { RoseTree.Node(42, new RoseLeaf<int, string>("bar")) }; yield return new[] { exampleTree }; } } [Theory, MemberData(nameof(BifunctorLawsData))] public void SelectNodeObeysFirstFunctorLaw(IRoseTree<int, string> t) { Assert.Equal(t, t.SelectNode(Id)); }

This test uses xUnit.net's `[Theory]`

feature to supply a small set of example input values. The input values are defined by the `BifunctorLawsData`

property, since I'll reuse the same values for all the bifunctor law demonstration tests. The `exampleTree`

object is the tree shown in Church-encoded rose tree.

The tests also use the identity function implemented as a `private`

function called `Id`

, since C# doesn't come equipped with such a function in the Base Class Library.

For all the `IRoseTree<int, string>`

objects `t`

, the test simply verifies that the original tree `t`

is equal to the tree projected over the first axis with the `Id`

function.

Likewise, the first functor law applies when translating over the second dimension:

[Theory, MemberData(nameof(BifunctorLawsData))] public void SelectLeafObeysFirstFunctorLaw(IRoseTree<int, string> t) { Assert.Equal(t, t.SelectLeaf(Id)); }

This is the same test as the previous test, with the only exception that it calls `SelectLeaf`

instead of `SelectNode`

.

Both `SelectNode`

and `SelectLeaf`

are implemented by `SelectBoth`

, so the real test is whether this method obeys the identity law:

[Theory, MemberData(nameof(BifunctorLawsData))] public void SelectBothObeysIdentityLaw(IRoseTree<int, string> t) { Assert.Equal(t, t.SelectBoth(Id, Id)); }

Projecting over both dimensions with the identity function does, indeed, return an object equal to the input object.

### Consistency law #

In general, it shouldn't matter whether you map with `SelectBoth`

or a combination of `SelectNode`

and `SelectLeaf`

:

[Theory, MemberData(nameof(BifunctorLawsData))] public void ConsistencyLawHolds(IRoseTree<int, string> t) { DateTime f(int i) => new DateTime(i); bool g(string s) => string.IsNullOrWhiteSpace(s); Assert.Equal(t.SelectBoth(f, g), t.SelectLeaf(g).SelectNode(f)); Assert.Equal( t.SelectNode(f).SelectLeaf(g), t.SelectLeaf(g).SelectNode(f)); }

This example creates two local functions `f`

and `g`

. The first function, `f`

, creates a new `DateTime`

object from an integer, using one of the `DateTime`

constructor overloads. The second function, `g`

, just delegates to `string.IsNullOrWhiteSpace`

, although I want to stress that this is just an example. The law should hold for any two (pure) functions.

The test then verifies that you get the same result from calling `SelectBoth`

as when you call `SelectNode`

followed by `SelectLeaf`

, or the other way around.

### Composition laws #

The composition laws insist that you can compose functions, or translations, and that again, the choice to do one or the other doesn't matter. Along each of the axes, it's just the second functor law applied. This parametrised test demonstrates that the law holds for `SelectNode`

:

[Theory, MemberData(nameof(BifunctorLawsData))] public void SecondFunctorLawHoldsForSelectNode(IRoseTree<int, string> t) { char f(bool b) => b ? 'T' : 'F'; bool g(int i) => i % 2 == 0; Assert.Equal( t.SelectNode(x => f(g(x))), t.SelectNode(g).SelectNode(f)); }

Here, `f`

is a local function that returns the the character `'T'`

for `true`

, and `'F'`

for `false`

; `g`

is the *even* function. The second functor law states that mapping `f(g(x))`

in a single step is equivalent to first mapping over `g`

and then map the result of that using `f`

.

The same law applies if you fix the first dimension and translate over the second:

[Theory, MemberData(nameof(BifunctorLawsData))] public void SecondFunctorLawHoldsForSelectLeaf(IRoseTree<int, string> t) { bool f(int x) => x % 2 == 0; int g(string s) => s.Length; Assert.Equal( t.SelectLeaf(x => f(g(x))), t.SelectLeaf(g).SelectLeaf(f)); }

Here, `f`

is the *even* function, whereas `g`

is a local function that returns the length of a string. Again, the test demonstrates that the output is the same whether you map over an intermediary step, or whether you map using only a single step.

This generalises to the composition law for `SelectBoth`

:

[Theory, MemberData(nameof(BifunctorLawsData))] public void SelectBothCompositionLawHolds(IRoseTree<int, string> t) { char f(bool b) => b ? 'T' : 'F'; bool g(int x) => x % 2 == 0; bool h(int x) => x % 2 == 0; int i(string s) => s.Length; Assert.Equal( t.SelectBoth(x => f(g(x)), y => h(i(y))), t.SelectBoth(g, i).SelectBoth(f, h)); }

Again, whether you translate in one or two steps shouldn't affect the outcome.

As all of these tests demonstrate, the bifunctor laws hold for rose trees. The tests only showcase five examples, but I hope it gives you an intuition how any rose tree is a bifunctor. After all, the `SelectNode`

, `SelectLeaf`

, and `SelectBoth`

methods are all generic, and they behave the same for all generic type arguments.

### Summary #

Rose trees are bifunctors. You can translate the node and leaf dimension of a rose tree independently of each other, and the bifunctor laws hold for any pure translation, no matter how you compose the projections.

As always, there can be performance differences between the various compositions, but the outputs will be the same regardless of composition.

A functor, and by extension, a bifunctor, is a structure-preserving map. This means that any projection preserves the structure of the underlying container. For rose trees this means that the shape of the tree remains the same. The number of leaves remain the same, as does the number of internal nodes.

**Next:** Software design isomorphisms.