Книга: Functional Programming in JavaScript
Назад: Arrays and functors
Дальше: Monads

Function compositions, revisited

Functions are another type of primitive that we can create a functor for. And that functor is called . We defined functors as something that takes a value from a container and applies a function to it. When that container is a function, we just call it to get its inner value.

We already know what function compositions are, but let's look at what they can do in a category theory-driven environment.

Function compositions are associative. If your high school algebra teacher was like mine, she taught you what the property is but not what it can do. In practice, compose is what the associative property can do.

Function compositions, revisited

Function compositions, revisited

We can do any inner-compose, it doesn't matter how it's grouped. This is not to be confused with the commutative property. ƒ o g does not always equal g o ƒ. In other words, the reverse of the first word of a string is not the same as the first word of the reverse of a string.

What this all means is that it doesn't matter which functions are applied and in what order, as long as the input of each functions comes from the output of the previous function. But wait, if the function on the right relies on the function on the left, then can't there be only one order of evaluation? Left to right? True, but if it's encapsulated, then we can control it however we feel fit. This is what empowered lazy evaluation in JavaScript.

Function compositions, revisited

Let's rewrite function composition, not as an extension of the function prototype, but as a stand-alone function that will allow us to get more out of it. The basic form is as follows:

var fcompose = function(f, g) {   return function() {     return f.call(this, g.apply(this, arguments));   }; };

But we'll need it to work on any number of inputs.

var fcompose = function() {   // first make sure all arguments are functions   var funcs = arrayOf(func)(arguments);    // return a function that applies all the functions   return function() {     var argsOfFuncs = arguments;     for (var i = funcs.length; i > 0; i -= 1) {       argsOfFuncs  = [funcs[i].apply(this, args)];     }     return args[0];   }; };  // example: var f = fcompose(negate, square, mult2, add1); f(2); // Returns: -36

Now that we've encapsulated the functions, we have control over them. We could rewrite the compose function such that each function accepts another function as input, stores it, and gives back an object that does the same. Instead of accepting an array as an input, doing something with it, and then giving back a new array for each operation, we can accept a single array for each element in the source, perform all operations combined (every , , and so on, composed together), and finally store the results in a new array. This is lazy evaluation via function composition. No reason to reinvent the wheel here. Many libraries have a nice implementation of this concept, including the , and libraries.

There's a lot more we can do as a result of this different model: asynchronous iteration, asynchronous event handling, lazy evaluation, and even automatic parallelization.

Note

Automatic parallelization? There's a word for that in the computer science industry: IMPOSSIBLE. But is it really impossible? The next evolutionary leap in Moore's law might be a compiler that parallelizes our code for us, and could function composition be it?

No, it doesn't quite work that way. The JavaScript engine is what is really doing the parallelization, not automatically but with well thought-out code. Compose just gives the engine the chance to split it into parallel processes. But that in itself is pretty cool.

Назад: Arrays and functors
Дальше: Monads

bsn
thank
Vesa Karvonen
I hope you don't mind, but I’d like to point you and your readers to my high-performance optics library for JavaScript that is in production use in multiple projects, has comprehensive support for partial optics and interactive documentation: https://calmm-js.github.io/partial.lenses/ (this takes a moment to load — be patient!)