## Explanation of liftA2 in JavaScript

April 16th, 2013This came out of the recent JS Promises debate. @ForbesLindesay was confused by liftA2, so I thought I’d try to explain it.

I’ll tweak @pufuwozu’s examples so that they return values.

// Promises (called when both succeed) liftA2(readFile('hello.txt'), readFile('world.txt'), function(hello, world) { return hello + ' ' + world; });

// Optional values (only inserted into database when both exist) liftA2(optionalUsername, optionalPassword, function(username, password) { return {username: username, password: password); });

// Arrays (function called for each element of each array) liftA2([1, 2, 3], [4, 5, 6], function(a, b) { return a + b; });

In the first example, we have 3 arguments:

- two Promises of Strings
- a function that takes 2 Strings and builds them into another string

In this instance, liftA2 returns a Promise of String. This Promise, when evaluated, evaluates the two input Promises and runs their resulting values through the input function.

In the second example, we have 3 arguments:

- two Options of Strings
- a function that takes 2 Strings and builds them into an Object.

In this instance, liftA2 returns an Option of Object. If both input Options are “some”, it runs them through the function to generate “some” of the resulting. If either input is “none”, the function is not called and “none” is returned.

In the third example, we have 3 arguments:

- two Arrays of Numbers
- a function that takes 2 Numbers and adds them together to form another Number.

In this instance, liftA2 returns an Array of Number. Each element in the resulting array is the sum of one Number from the first Array and one Number from the second Array. Each combination of an element in the first and the second is run through the function.

Let’s look at those types (using Haskell type syntax, as JavaScript doesn’t have types). a -> b just means a function that takes an ‘a’ and returns a ‘b’. The brackets denote ‘tuples’ – approximating JavaScript argument lists.

- (Promise String, Promise String, (String, String) -> String)) -> Promise String
- (Option String, Option String, (String, String) -> Object)) -> Option Object
- (Array Number, Array Number, (Number, Number) -> Number)) -> Array Number

Note that there is ONE implementation of liftA2, not one for each datatype. Just one. So, how can one function deal with data of vastly different type? It all comes down to a pattern – Applicatives.

Promise, Option and Array are all Applicatives (as are all Monads). liftA2 takes two arguments of the same type of Applicative, with the same parameter type (actually the universal type in javascript). It “extracts” the values from the Applicatives, runs them through the function, then “boxes them back up” into a value of the applicative type.

So, what does it mean to “extract” and “box back up”? Well, that depends on the Applicative! It’s what makes one Applicative different from another! That’s exactly why liftA2 can apply to such vastly different data structures – because the relevant differences have been encapsulated in their implementation of Applicative.

So, what’s the type of this liftA2 function in JavaScript?

for all Applicatives f. for all types a, b, c. (f a, f b, f c, (a, b) -> c) -> f c

If you look at the types above, you’ll notice they all match this pattern, hence they all meet the requirements to call liftA2.

So, what does it require to be an Applicative?

- You need to be a Functor, so you need a ‘map’ function.
- You need a “point” function that wraps a value in the applicative.
- You need an “ap” function of this type: for your Applicative f. for all types a, b, c. (f a, f b, f ((a, b) -> c)) -> f c

And, what does liftA2 look like? Courtesy of @pufuwozu

function liftA2(pa, pb, f) { return ap(pb, map(pa, function(a) { return function(b) { return f(a, b); }; })); }

That’s it? Really? That’s all it takes? For real. So much power, so little code. That’s the power of abstractions.