Yoneda and Coyoneda (Lazy Transformations)
At this point, assuming of course that you have followed along with a few of the functional threads, you should be somewhat familiar with higher order abstractions like Functors, Applicatives and Monads, namely:
- Map (Select in Linq)
- Apply (No equivalent in Linq)
- FlatMap (SelectMany in Linq)
Which means that introducing you to some new higher order abstractions like
Yoneda and
Coyoneda should not be not be an impossible ask.
It is however just an inkling into the world
Free Objects with abstractions like:
- Free Monoids
- Free Functors
- Free Applicative Functors
- Free Monads
Framing the problem
To understand the need for something like a
Yoneda or
Coyoneda it would be best to first frame a standard computational problem when using Higher Order Functiions like Linq, for example:
C#:
var input = Enumerable.Range(0, 1000000);
var result = input.map(f).map(g).map(h);
Here we are mapping over (I.e. transforming) the 1 million elements contained in the input list, 3 times, once for each function
f, then
g, and finally
h.
It really doesn't matter what these transformations represent, but if it helps with the understanding think of them as follows:
C#:
Func<int, int> f = x => x + 1; // Increment
Func<int, int> g = x => x * 2; // Double the value
Func<int, int> h = x => x - 1; // Decrement
The mapping over input first by
f, then by
g, and finally
h results in the computation looping over each of elements in input 3 times, which isn't a very efficient way to compute the result.
It would be far more efficient to perform these 3 transformations in 1 step, for example:
C#:
var result = input.map(x => h(g(f(x))));
I.e. the single composition of functions
f,
g, and
h, which effectively loops only once over the elements in the input list.
Whilst this may appear to be an easy enough solution, in practice things rarely are this easy when it comes to more complex computation scenarios. Plus if we syntactically compare the 2 variations of the same result:
C#:
var result = input.map(f).map(g).map(h);
var result = input.map(x => h(g(f(x))));
Which one is is syntactically less complex. Considering more complex transformations scenarios than just
f,
g, and
h, I would say the 1st one, because it is syntactically quite obvious that we are applying 3 transformation, 1 after the other to the elements of input, whereas with the 2nd example it's not quite so clear, we need to think in reverse, or inside out.
Naturally you may agree or disagree here, and that's fine.
However let's frame what could be happening in the 2nd scenario. Effectively we are applying transformations to the elements but delaying their computation until we have composed together all the computations that we require for the result.
Now imagine if we could use this syntax:
Code:
var result = input.map(f).map(g).map(h);
...and still delay the final computation in the same way as the compositional variant did. Would that be something interesting or useful?
That essentially describes what the
Yoneda Product in homological algebra achieves. It allows us to lazily, defer the computation of transformations of elements until we are ready to compute the result, and it reduces the overall complexity of a computation by ensuring that we loop only once over the elements as opposed to once per transformation.
This is also the essence of what constitutes a
Free Object in the mathematical sense, and should help to give you an inkling into why more complex abstractions like
Free Monads might exist; ...because they are in part based on an abstractions like the
Yoneda Lemma, which has been described as the hardest trivial abstraction in mathematics. Whilst the proof of the lemma is not too difficult to understand, it has significance in a wide variety of areas, and corresponds with group theory's
Cayley’s theorem.
Additional References:
The goal of this thread will be to make this syntactical variant:
C#:
var result = input.map(f).map(g).map(h);
..have the same compute cost as this compositional variant:
C#:
var result = input.map(x => h(g(f(x))));
Note:
Next post should be up in a day or two.