We're going to ignore the OO stuff altogether and focus on one application of first-class functions.
Functional Composition chains together functions to create a new function.
There are many ways to create compose
functions. We'll
examine a few of them.
We will glance at the implementations, but mostly focus on the APIs presented by these techniques.
We will use these functions for our tests throughout:
Question: What value will this code yield?
In mathematics f ∘ g
(pronounced "f composed with g") is the function that given x
,
returns f(g(x))
.
So if we follow the mathematical model compose(add1, square)(x)
should equal add1(square(x))
.
This naive implementation has at least two flaws:
this
context used to call it.But it's simple to fix those:
We can make composition a property of all functions by modifying the Function prototype.
This example doesn't work in this presentation sandbox, but will work in browser. It can be demoed in the console.
There are those who will tell you not to alter the native prototypes, and perhaps they have good points, but ...
It's easy enough to extend this to multiple functions:
This is very similar to Underscore's code.
We could do this with the prototype
version as well, although that might feel strange.
Our examples have used functions that returned the same type they accepted. This is not important. The essential feature is that each function accept the same type its predecessor generated:
Reading these from right-to-left is... odd. There are several options:
compose(f, g) ~~> g(f(x)
)
But this won't match many people's expectations of compose
.
pipe
or sequence
function to do the reverse of compose
.
But this expands the API surface area without adding functionality
compose
with a different enough API that no one expects the mathematical behavior.We'll look at some ways of doing this next.
This API makes it more obvious that the functions should be read from left-to-right:
We can make the ordering of functions entirely explict:
This API makes a miniature DSL out of functional composition, always starting with "first", followed by any number of "then", "andThen", or "next" clauses, optionally concluding with "finally", "last", or "andFinally".
It is more verbose, but is extremely explicit.
What if we overloaded the right-shift operator?
This doesn't work in this presentation sandbox, but will work in browser. It can be demoed in the console.
Operator overloading in JS? WTF?.
Fake it until you make it!
This doesn't work in this presentation sandbox, but will work in browser. It can be demoed in the console.
This is just a fun exercise to show what can be done. Please don't try this at home!
To mathematicians, functional composition is a simple concept. Translating it into code can lead to some interesting and complex API decisions.
There is clearly no right answer. Except for the operator overloading one, every one of those implementations has a claim to be the one you might choose.
And that's it except for ...
/
#