Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Category Theory

Save for later
  • 7 min read
  • 24 Mar 2015

article-image

In this article written by Dan Mantyla, author of the book Functional Programming in JavaScript, you would get to learn about some of the JavaScript functors such as Maybes, Promises and Lenses.

(For more resources related to this topic, see here.)


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.

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:

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at £15.99/month. Cancel anytime
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'

Summary


In this article, we got to learn some of the different functors that are used in JavaScript such as Maybes, Promises and Lenses along with their corresponding examples, thus making it easy for us to understand the concept clearly.

Resources for Article:





Further resources on this subject: