Have you noticed the following weird fact: as you get better at JavaScript, your code has fewer and fewer returned values? Remember your initial procedural code:
1 | function foo() { |
As you move away from procedural to asynchronous code (see Journey from procedural to reactive JavaScript with stops), notice that promises move away from the returned value to resolved value
1 | foo().then(function (result) { |
The next step from promises is event emitters, that break the link between calling a function and generating a value.
1 | aFoo.on('foo', function () { |
There is no longer any value tied specifically to emitting the event 'foo' in line // 1
. The console output
in line // 2
could have been produced by any call to aFoo.emit('foo');
. Compared to promise-returning functions,
we no longer can even schedule the next computation step to occur after line // 2
has executed.
Is this a good thing? At some level yes - we are decoupling calling a function from having its result immediately (promises), or from knowing that the specific result has been produced (event emitters). On the other hand, this change is difficult to understand and adjust if you have not used asynchronous and functional programming enough.
On the other hand, you can adjust your expectations. Instead of producing a value, anything you call will execute some code. Using synchronous returned value forced the code to be written after the call. With promise-returning or event-emitting code, the code can be anywhere, giving you the total freedom.
A good transition from procedural code is to write the initial code as a composition of functions
1 | // procedural |
Once written in this point-free style, it is natural to transform it to async code resolving value instead of returning it.
1 | var Q = require('q'); |
Functional libraries, like Ramda now include methods like pipeP to compose promise-returning methods, similar to sync methods. For example, a pipeline of sync and async methods could be written
1 | // sync foo, print |
Going in the opposite direction, we might get a wrapped value from methods that traditionally do not return anything. For example when using event emitters, we can get back a promise with a little extra programming, see Promisify event emitter. Or we could use generators to recreate the synchronous-looking code, see database example.
Wrapping guard conditions
Asynchronous results are not the only values we can wrap, removing them from the "normal" source code flow.
Often we guard against invalid data inputs using if (variable) { ... }
blocks. We can wrap
the variable
into an object, and replace if
JavaScript keyword with a function call, and the statements
to execute { ... }
with a function to apply. This is how we get a Maybe functor.
1 | function Maybe(value, fn) { |
Notice that we could use foo
and statements directly before
1 | var foo = ...; |
But now we have to use variable foo
indirectly
1 | var foo = ...; |
I use this principle to check the input variables a lot, for example see our check-more-types library.
1 | function isSum10(a, b) { |
Next step
Notice that Maybe
and check.then
are functions that take a value and return either undefined
or fn(value)
.
What if we wanted to combine multiple functors? It would be nice if a functor Maybe
returned another Maybe
,
just like Promise.then()
returns another promise so we can keep chaining them.
Turns out this approach exists and the objects that wrap a value and can be chained are called Monads. An excellent explanation and derivation of wrapping a value in a Functor, then in a Monad can be found in this blog post "From callback to (Future -> Functor -> Monad)".