Category Theory - Functional Programming in JavaScript (2015)

Functional Programming in JavaScript (2015)

Chapter 5. Category Theory

Thomas Watson was famously quoted as saying, "I think there is a world market for maybe five computers". That was in 1948. Back then, everybody knew that computers would only be used for two things: math and engineering. Not even the biggest minds in tech could predict that, one day, computers would be able to translate Spanish to English, or simulate entire weather systems. At the time, the fastest machine was IBM's SSEC, clocking in at 50 multiplications per second, the display terminal wasn't due until 15 years later and multiple-processing meant multiple user terminals sharing a single processor. The transistor changed everything, but tech's visionaries still missed the mark. Ken Olson made another famously foolish prediction when, in 1977, he said "There is no reason anyone would want a computer in their home".

It seams obvious to us now that computers are not just for scientists and engineers, but that's hindsight. The idea that machines can do more than just math was anything but intuitive 70 years ago. Watson didn't just fail to realize how computers could transform a society, he failed to realize the transformative and evolving powers of mathematics.

But the potential of computers and math was not lost on everybody. John McCarthy invented Lisp in 1958, a revolutionary algorithm-based language that ushered in a new era in computing. Since its inception, Lisp was instrumental in the idea of using abstraction layers—compilers, interpreters, virtualization—to push forward the progression of computers from hardcore math machines to what they are today.

From Lisp came Scheme, a direct ancestor of JavaScript. Now that brings us full circle. If computers are, at their core, machines that just do math, then it stands to reason that a math-based programming paradigm would excel.

The term "math" is being used here not to describe the "number crunching" that computers can obviously do, but to describe discrete mathematics: the study of discrete, mathematical structures such as statements in logic or the instructions of a computer language. By treating code as a discrete mathematical structure, we can apply concepts and ideas in math to it. This is what has made functional programming so instrumental in artificial intelligence, graph search, pattern recognition and other big challenges in computer science.

In this chapter, we will experiment with some of these concepts and their applications in everyday programming challenges. They will include:

· Category theory

· Morphisms

· Functors

· Maybes

· Promises

· Lenses

· Function composition

With these concepts, we'll be able to write entire libraries and APIs very easily and safely. And we'll go from explaining category theory to formally implementing it in JavaScript.

Category theory

Category theory is the theoretical concept that empowers function composition. Category theory and function composition go together like engine displacement and horsepower, like NASA and the space shuttle, like good beer and a mug to pour it in. Basically, you can't have one without the other.

Category theory in a nutshell

Category theory really isn't too difficult a concept. Its place in math is large enough to fill up an entire graduate-level college course, but its place in computer programming can be summed up quite easily.

Einstein once said, "If you can't explain it to a 6-year-old, you don't know it yourself". Thus, in the spirit of explaining it to a 6-year-old, category theory is just connecting the dots. Although it may be grossly over-simplifying category theory, it does do a good job of explaining what we need to know in a straightforward manner.

First you'll need to know some terminology. Categories are just sets with the same type. In JavaScript, they're arrays or objects that contain variables that are explicitly declared as numbers, strings, Booleans, dates, nodes, and so on. Morphisms are pure functions that, when given a specific set of inputs, always return the same output. Homomorphic operations are restricted to a single category, while polymorphic operations can operate on multiple categories. For example, the homomorphic function multiplication only works on numbers, but the polymorphic function addition can work on strings too.

Category theory in a nutshell

The following diagram shows three categories—A, B, and C—and two morphisms—ƒ and ɡ.

Category theory tells us that, when we have two morphisms where the category of the first one is the expected input of the other, then they can be composed to the following:

Category theory in a nutshell

The ƒ o g symbol is the composition of morphisms ƒ and g. Now we can just connect the dots.

Category theory in a nutshell

And that's all it really is, just connecting dots.

Type safety

Let's connect some dots. Categories contain two things:

1. Objects (in JavaScript, types).

2. Morphisms (in JavaScript, pure functions that only work on types).

These are the terms given to category theory by mathematicians, so there is some unfortunate nomenclature overloading with our JavaScript terminology. Objects in category theory are more like variables with an explicit data type and not collections of properties and values like in the JavaScript definition of objects. Morphisms are just pure functions that use those types.

So applying the idea of category theory to JavaScript is pretty easy. Using category theory in JavaScript means working with one certain data type per category. Data types are numbers, strings, arrays, dates, objects, Booleans, and so on. But, with no strict type system in JavaScript, things can go awry. So we'll have to implement our own method of ensuring that the data is correct.

There are four primitive data types in JavaScript: numbers, strings, Booleans, and functions. We can create type safety functions that either return the variable or throw an error. This fulfils the object axiom of categories.

var str = function(s) {

if (typeof s === "string") {

return s;

}

else {

throw new TypeError("Error: String expected, " + typeof s + " given.");

}

}

var num = function(n) {

if (typeof n === "number") {

return n;

}

else {

throw new TypeError("Error: Number expected, " + typeof n + " given.");

}

}

var bool = function(b) {

if (typeof b === "boolean") {

return b;

}

else {

throw new TypeError("Error: Boolean expected, " + typeof b + " given.");

}

}

var func = function(f) {

if (typeof f === "function") {

return f;

}

else {

throw new TypeError("Error: Function expected, " + typeof f + " given.");

}

}

However, there's a lot of repeated code here and that isn't very functional. Instead, we can create a function that returns another function that is the type safety function.

var typeOf = function(type) {

return function(x) {

if (typeof x === type) {

return x;

}

else {

throw new TypeError("Error: "+type+" expected, "+typeof x+" given.");

}

}

}

var str = typeOf('string'),

num = typeOf('number'),

func = typeOf('function'),

bool = typeOf('boolean');

Now, we can use them to ensure that our functions behave as expected.

// unprotected method:

var x = '24';

x + 1; // will return '241', not 25

// protected method

// plusplus :: Int -> Int

function plusplus(n) {

return num(n) + 1;

}

plusplus(x); // throws error, preferred over unexpected output

Let's look at a meatier example. If we want to check the length of a Unix timestamp that is returned by the JavaScript function Date.parse(), not as a string but as a number, then we'll have to use our str() function.

// timestampLength :: String -> Int

function timestampLength(t) { return num(str(t).length); }

timestampLength(Date.parse('12/31/1999')); // throws error

timestampLength(Date.parse('12/31/1999')

.toString()); // returns 12

Functions like this that explicitly transform one type to another (or to the same type) are called morphisms. This fulfils the morphism axiom of category theory. These forced type declarations via the type safety functions and the morphisms that use them are everything we need to represent the notion of a category in JavaScript.

Object identities

There's one other important data type: objects.

var obj = typeOf('object');

obj(123); // throws error

obj({x:'a'}); // returns {x:'a'}

However, objects are different. They can be inherited. Everything that is not a primitive—numbers, strings, Booleans, and functions—is an object, including arrays, dates, elements, and more.

There's no way to know what type of object something is, as in to know what sub-type a JavaScript 'object' is, from the typeof keyword, so we'll have to improvise. Objects have a toString() function that we can hijack for this purpose.

var obj = function(o) {

if (Object.prototype.toString.call(o)==="[object Object]") {

return o;

}

else {

throw new TypeError("Error: Object expected, something else given.");

}

}

Again, with all the objects out there, we should implement some code re-use.

var objectTypeOf = function(name) {

return function(o) {

if (Object.prototype.toString.call(o) === "[object "+name+"]") {

return o;

}

else {

throw new TypeError("Error: '+name+' expected, something else given.");

}

}

}

var obj = objectTypeOf('Object');

var arr = objectTypeOf('Array');

var date = objectTypeOf('Date');

var div = objectTypeOf('HTMLDivElement');

These will be very useful for our next topic: functors.

Functors

While morphisms are mappings between types, functors are mappings between categories. They can be thought of as functions that lift values out of a container, morph them, and then put them into a new container. The first input is a morphism for the type and the second input is the container.

Note

The type signature for functors looks like this:

// myFunctor :: (a -> b) -> f a -> f b

This says, "give me a function that takes a and returns b and a box that contains a(s), and I'll return a box that contains b(s).

Creating functors

It turns out we already have one functor: map(). It grabs the values within the container, an array, and applies a function to it.

[1, 4, 9].map(Math.sqrt); // Returns: [1, 2, 3]

However, we'll need to write it as a global function and not as a method of the array object. This will allow us to write cleaner, safer code later on.

// map :: (a -> b) -> [a] -> [b]

var map = function(f, a) {

return arr(a).map(func(f));

}

This example seems like a contrived wrapper because we're just piggybacking onto the map() function. But it serves a purpose. It provides a template for maps of other types.

// strmap :: (str -> str) -> str -> str

var strmap = function(f, s) {

return str(s).split('').map(func(f)).join('');

}

// MyObject#map :: (myValue -> a) -> a

MyObject.prototype.map(f{

return func(f)(this.myValue);

}

Arrays and functors

Arrays are the preferred way to work with data in functional JavaScript.

Is there an easier way to create functors that are already assigned to a morphism? Yes, and it's called arrayOf. When you pass in a morphism that expects an integer and returns an array, you get back a morphism that expects an array of integers and returns an array of arrays.

It is not a functor itself, but it allows us to create functors from morphisms.

// arrayOf :: (a -> b) -> ([a] -> [b])

var arrayOf = function(f) {

return function(a) {

return map(func(f), arr(a));

}

}

Here's how to create functors by using morphism:

var plusplusall = arrayOf(plusplus); // plusplus is our morphism

console.log( plusplusall([1,2,3]) ); // returns [2,3,4]

console.log( plusplusall([1,'2',3]) ); // error is thrown

The interesting property of the arrayOf functor is that it works on type safeties as well. When you pass in the type safety function for strings, you get back a type safety function for an array of strings. The type safeties are treated like the identity function morphism. This can be very useful for ensuring that an array contains all the correct types.

var strs = arrayOf(str);

console.log( strs(['a','b','c']) ); // returns ['a','b','c']

console.log( strs(['a',2,'c']) ); // throws error

Function compositions, revisited

Functions are another type of primitive that we can create a functor for. And that functor is called fcompose. 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 map(), filter(), 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 Lazy.js, Bacon.js and wu.js 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.

Monads

Monads are tools that help you compose functions.

Like primitive types, monads are structures that can be used as the containers that functors "reach into". The functors grab the data, do something to it, put it into a new monad, and return it.

There are three monads we'll focus on:

· Maybes

· Promises

· Lenses

So in addition to arrays (map) and functions (compose), we'll have five functors (map, compose, maybe, promise and lens). These are just some of the many other functors and monads that are out there.

Maybes

Maybes allow us to gracefully work with data that might be null and to have defaults. A maybe is a variable that either has some value or it doesn't. And it doesn't matter to the caller.

On its own, it might seem like this is not that big a deal. Everybody knows that null-checks are easily accomplished with an if-else statement:

if (getUsername() == null ) {

username = 'Anonymous') {

else {

username = getUsername();

}

But with functional programming, we're breaking away from the procedural, line-by-line way of doing things and instead working with pipelines of functions and data. If we had to break the chain in the middle just to check if the value existed or not, we would have to create temporary variables and write more code. Maybes are just tools to help us keep the logic flowing through the pipeline.

To implement maybes, we'll first need to create some constructors.

// the Maybe monad constructor, empty for now

var Maybe = function(){};

// the None instance, a wrapper for an object with no value

var None = function(){};

None.prototype = Object.create(Maybe.prototype);

None.prototype.toString = function(){return 'None';};

// now we can write the `none` function

// saves us from having to write `new None()` all the time

var none = function(){return new None()};

// and the Just instance, a wrapper for an object with a value

var Just = function(x){return this.x = x;};

Just.prototype = Object.create(Maybe.prototype);

Just.prototype.toString = function(){return "Just "+this.x;};

var just = function(x) {return new Just(x)};

Finally, we can write the maybe function. It returns a new function that either returns nothing or a maybe. It is a functor.

var maybe = function(m){

if (m instanceof None) {

return m;

}

else if (m instanceof Just) {

return just(m.x);

}

else {

throw new TypeError("Error: Just or None expected, " + m.toString() + " given.");

}

}

And we can also create a functor generator just like we did with arrays.

var maybeOf = function(f){

return function(m) {

if (m instanceof None) {

return m;

}

else if (m instanceof Just) {

return just(f(m.x));

}

else {

throw new TypeError("Error: Just or None expected, " + m.toString() + " given.");

}

}

}

So Maybe is a monad, maybe is a functor, and maybeOf returns a functor that is already assigned to a morphism.

We'll need one more thing before we can move forward. We'll need to add a method to the Maybe monad object that helps us use it more intuitively.

Maybe.prototype.orElse = function(y) {

if (this instanceof Just) {

return this.x;

}

else {

return y;

}

}

In its raw form, maybes can be used directly.

maybe(just(123)).x; // Returns 123

maybeOf(plusplus)(just(123)).x; // Returns 124

maybe(plusplus)(none()).orElse('none'); // returns 'none'

Anything that returns a method that is then executed is complicated enough to be begging for trouble. So we can make it a little cleaner by calling on our curry() function.

maybePlusPlus = maybeOf.curry()(plusplus);

maybePlusPlus(just(123)).x; // returns 123

maybePlusPlus(none()).orElse('none'); // returns none

But the real power of maybes will become clear when the dirty business of directly calling the none() and just() functions is abstracted. We'll do this with an example object User, that uses maybes for the username.

var User = function(){

this.username = none(); // initially set to `none`

};

User.prototype.setUsername = function(name) {

this.username = just(str(name)); // it's now a `just

};

User.prototype.getUsernameMaybe = function() {

var usernameMaybe = maybeOf.curry()(str);

return usernameMaybe(this.username).orElse('anonymous');

};

var user = new User();

user.getUsernameMaybe(); // Returns 'anonymous'

user.setUsername('Laura');

user.getUsernameMaybe(); // Returns 'Laura'

And now we have a powerful and safe way to define defaults. Keep this User object in mind because we'll be using it later on in this chapter.

Promises

The nature of promises is that they remain immune to changing circumstances.

- Frank Underwood, House of Cards

In functional programming, we're often working with pipelines and data flows: chains of functions where each function produces a data type that is consumed by the next. However, many of these functions are asynchronous: readFile, events, AJAX, and so on. Instead of using a continuation-passing style and deeply nested callbacks, how can we modify the return types of these functions to indicate the result? By wrapping them in promises.

Promises are like the functional equivalent of callbacks. Obviously, callbacks are not all that functional because, if more than one function is mutating the same data, then there can be race conditions and bugs. Promises solve that problem.

You should use promises to turn this:

fs.readFile("file.json", function(err, val) {

if( err ) {

console.error("unable to read file");

}

else {

try {

val = JSON.parse(val);

console.log(val.success);

}

catch( e ) {

console.error("invalid json in file");

}

}

});

Into the following code snippet:

fs.readFileAsync("file.json").then(JSON.parse)

.then(function(val) {

console.log(val.success);

})

.catch(SyntaxError, function(e) {

console.error("invalid json in file");

})

.catch(function(e){

console.error("unable to read file")

});

The preceding code is from the README for bluebird: a full featured Promises/A+ implementation with exceptionally good performance. Promises/A+ is a specification for implementing promises in JavaScript. Given its current debate within the JavaScript community, we'll leave the implementations up to the Promises/A+ team, as it is much more complex than maybes.

But here's a partial implementation:

// the Promise monad

var Promise = require('bluebird');

// the promise functor

var promise = function(fn, receiver) {

return function() {

var slice = Array.prototype.slice,

args = slice.call(arguments, 0, fn.length - 1),

promise = new Promise();

args.push(function() {

var results = slice.call(arguments),

error = results.shift();

if (error) promise.reject(error);

else promise.resolve.apply(promise, results);

});

fn.apply(receiver, args);

return promise;

};

};

Now we can use the promise() functor to transform functions that take callbacks into functions that return promises.

var files = ['a.json', 'b.json', 'c.json'];

readFileAsync = promise(fs.readFile);

var data = files

.map(function(f){

readFileAsync(f).then(JSON.parse)

})

.reduce(function(a,b){

return $.extend({}, a, b)

});

Lenses

Another reason why programmers really like monads is that they make writing libraries very easy. To explore this, let's extend our User object with more functions for getting and setting values but, instead of using getters and setters, we'll use lenses.

Lenses are first-class getters and setters. They allow us to not just get and set variables, but also to run functions over it. But instead of mutating the data, they clone and return the new data modified by the function. They force data to be immutable, which is great for security and consistency as well for libraries. They're great for elegant code no matter what the application, so long as the performance-hit of introducing additional array copies is not a critical issue.

Before we write the lens() function, let's look at how it works.

var first = lens(

function (a) { return arr(a)[0]; }, // get

function (a, b) { return [b].concat(arr(a).slice(1)); } // set

);

first([1, 2, 3]); // outputs 1

first.set([1, 2, 3], 5); // outputs [5, 2, 3]

function tenTimes(x) { return x * 10 }

first.modify(tenTimes, [1,2,3]); // outputs [10,2,3]

And here's how the lens() function works. It returns a function with get, set and mod defined. The lens() function itself is a functor.

var lens = fuction(get, set) {

var f = function (a) {return get(a)};

f.get = function (a) {return get(a)};

f.set = set;

f.mod = function (f, a) {return set(a, f(get(a)))};

return f;

};

Let's try an example. We'll extend our User object from the previous example.

// userName :: User -> str

var userName = lens(

function (u) {return u.getUsernameMaybe()}, // get

function (u, v) { // set

u.setUsername(v);

return u.getUsernameMaybe();

}

);

var bob = new User();

bob.setUsername('Bob');

userName.get(bob); // returns 'Bob'

userName.set(bob, 'Bobby'); //return 'Bobby'

userName.get(bob); // returns 'Bobby'

userName.mod(strToUpper, bob); // returns 'BOBBY'

strToUpper.compose(userName.set)(bob, 'robert'); // returns 'ROBERT'

userName.get(bob); // returns 'robert'

jQuery is a monad

If you think all this abstract babble about categories, functors, and monads has no real-world application, think again. jQuery, the popular JavaScript library that provides an enhanced interface for working with HTML is, in-fact, a monadic library.

The jQuery object is a monad and its methods are functors. Really, they're a special type of functor called endofunctors. Endofunctors are functors that return the same category as the input, that is, F :: X -> X. Each jQuery method takes a jQuery object and returns a jQuery object, which allows methods to be chained, and they will have the type signature jFunc :: jquery-obj -> jquery-obj.

$('li').add('p.me-too').css('color', 'red').attr({id:'foo'});

This is also what empowers jQuery's plugin framework. If the plugin takes a jQuery object as input and returns one as output, then it can be inserted into the chain.

Let's look at how jQuery was able to implement this.

Monads are the containers that the functors "reach into" to get the data. In this way, the data can be protected and controlled by the library. jQuery provides access to the underlying data, a wrapped set of HTML elements, via its many methods.

The jQuery object itself is written as the result of an anonymous function call.

var jQuery = (function () {

var j = function (selector, context) {

var jq-obj = new j.fn.init(selector, context);

return jq-obj;

};

j.fn = j.prototype = {

init: function (selector, context) {

if (!selector) {

return this;

}

}

};

j.fn.init.prototype = j.fn;

return j;

})();

In this highly simplified version of jQuery, it returns a function that defines the j object, which is actually just an enhanced init constructor.

var $ = jQuery(); // the function is returned and assigned to `$`

var x = $('#select-me'); // jQuery object is returned

In the same way that functors lift values out of a container, jQuery wraps the HTML elements and provides access to them as opposed to modifying the HTML elements directly.

jQuery doesn't advertise this often, but it has its own map() method for lifting the HTML element objects out of the wrapper. Just like the fmap() method, the elements are lifted, something is done with them, and then they're placed back into the container. This is how many of jQuery's commands work in the backend.

$('li').map(function(index, element) {

// do something to the element

return element

});

Another library for working with HTML elements, Prototype, does not work like this. Prototype alters the HTML elements directly via helpers. Consequently, it has not faired as well in the JavaScript community.

Implementing categories

It's about time we formally defined category theory as JavaScript objects. Categories are objects (types) and morphisms (functions that only work on those types). It's an extremely high-level, totally-declarative way to program, but it ensures that the code is extremely safe and reliable—perfect for APIs and libraries that are worried about concurrency and type safety.

First, we'll need a function that helps us create morphisms. We'll call it homoMorph() because they'll be homomorphisms. It will return a function that expects a function to be passed in and produces the composition of it, based on the inputs. The inputs are the types that the morphism accepts as input and gives as output. Just like our type signatures, that is, // morph :: num -> num -> [num], only the last one is the output.

var homoMorph = function( /* input1, input2,..., inputN, output */ ) {

var before = checkTypes(arrayOf(func)(Array.prototype.slice.call(arguments, 0, arguments.length-1)));

var after = func(arguments[arguments.length-1])

return function(middle) {

return function(args) {

return after(middle.apply(this, before([].slice.apply(arguments))));

}

}

}

// now we don't need to add type signature comments

// because now they're built right into the function declaration

add = homoMorph(num, num, num)(function(a,b){return a+b})

add(12,24); // returns 36

add('a', 'b'); // throws error

homoMorph(num, num, num)(function(a,b){

return a+b;

})(18, 24); // returns 42

The homoMorph() function is fairly complex. It uses a closure (see Chapter 2, Fundamentals of Functional Programming) to return a function that accepts a function and checks its input and output values for type safety. And for that, it relies on a helper function: checkTypes, which is defined as follows:

var checkTypes = function( typeSafeties ) {

arrayOf(func)(arr(typeSafeties));

var argLength = typeSafeties.length;

return function(args) {

arr(args);

if (args.length != argLength) {

throw new TypeError('Expected '+ argLength + ' arguments');

}

var results = [];

for (var i=0; i<argLength; i++) {

results[i] = typeSafeties[i](args[i]);

}

return results;

}

}

Now let's formally define some homomorphisms.

var lensHM = homoMorph(func, func, func)(lens);

var userNameHM = lensHM(

function (u) {return u.getUsernameMaybe()}, // get

function (u, v) { // set

u.setUsername(v);

return u.getUsernameMaybe();

}

)

var strToUpperCase = homoMorph(str, str)(function(s) {

return s.toUpperCase();

});

var morphFirstLetter = homoMorph(func, str, str)(function(f, s) {

return f(s[0]).concat(s.slice(1));

});

var capFirstLetter = homoMorph(str, str)(function(s) {

return morphFirstLetter(strToUpperCase, s)

});

Finally, we can bring it on home. The following example includes function composition, lenses, homomorphisms, and more.

// homomorphic lenses

var bill = new User();

userNameHM.set(bill, 'William'); // Returns: 'William'

userNameHM.get(bill); // Returns: 'William'

// compose

var capatolizedUsername = fcompose(capFirstLetter,userNameHM.get);

capatolizedUsername(bill, 'bill'); // Returns: 'Bill'

// it's a good idea to use homoMorph on .set and .get too

var getUserName = homoMorph(obj, str)(userNameHM.get);

var setUserName = homoMorph(obj, str, str)(userNameHM.set);

getUserName(bill); // Returns: 'Bill'

setUserName(bill, 'Billy'); // Returns: 'Billy'

// now we can rewrite capatolizeUsername with the new setter

capatolizedUsername = fcompose(capFirstLetter, setUserName);

capatolizedUsername(bill, 'will'); // Returns: 'Will'

getUserName(bill); // Returns: 'will'

The preceding code is extremely declarative, safe, reliable, and dependable.

Note

What does it mean for code to be declarative? In imperative programming, we write sequences of instructions that tell the machine how to do what we want. In functional programming, we describe relationships between values that tell the machine what we want it to compute, and the machine figures out the instruction sequences to make it happen. Functional programming is declarative.

Entire libraries and APIs can be constructed this way that allow programmers to write code freely without worrying about concurrency and type safety because those worries are handled in the backend.

Summary

About one in every 2,000 people has a condition known as synesthesia, a neurological phenomenon in which one sensory input bleeds into another. The most common form involves assigning colors with letters. However, there is an even rarer form where sentences and paragraphs are associated with tastes and feelings.

For these people, they don't read word by word, sentence by sentence. They look at the whole page/document/program and get a sense for how it tastes—not in the mouth but in the mind. Then they put the parts of the text together like the pieces of a puzzle.

This is what it is like to write fully declarative code: code that describes the relationships between values that tells the machine what we want it to compute. The parts of the program are not instructions in line-by-line order. Synesthetics may be able to do it naturally, but with a little practice anyone can learn how to put the relational puzzle pieces together.

In this chapter, we looked at several mathematical concepts that apply to functional programming and how they allow us to build relationships between data. Next, we'll explore recursion and other advanced topics in JavaScript.