Understanding composition
Function composition is the pointwise application of one function to the result of another to produce a third function., says Wikipedia. Here I show what I was getting wrong about it.
Having the two functions:
We can compose them as (f . g)
, since f
’s input type is g
’s output type.
Example
Say we have:
Then we can compose them as:
My mistake
I knew that composition was lazy:
But I missed how composition really works.
See, I always thought that the right side of the composition (in the example above, the function g
) was the first to be computed, but that is wrong.
It turns out, as it makes sense to be, that f
is the first function to be computed. This may sound odd to you as if f
expects Char
. But that’s obviously not the case.
Remember that Haskell is lazy. In a strict language, the composition would most likely happen as I was expecting it to happen but not in a lazy language.
Note that g(x)
is the value that f
receives, thus g
cannot be evaluated before (entering) f
as that would mean strict evaluation of arguments.
Proof
Let’s show that the left most function is the first to be computed.
Having these definitions for f
and g
:
An application based on their composition results in *** Exception: failed in f
:
Usefulness
One quick example, out of infinite possible ones:
When building Wai.Application
middlewares, the order in which you process a request does matter.
For instance, you want to first ensure the request is not targeting some protected endpoint before authenticating it, so to save unnecessary computation in a positive case.
In a strict language, handling such dependency while being able to compose wouldn’t be easy and clean. In Haskell, however, you simply need to set the order of the composition so that it matches your business logic, without having each middleware knowing about each other and falling into a dependency hell.
Simple but fundamental and useful piece of knowledge to keep in mind.