DEV Community 👩‍💻👨‍💻

gmlion
gmlion

Posted on • Originally published at Medium on

Lambda calculus and currying in Javascript

Photo by Aleks Dahlberg on Unsplash

Javascript as a language has always been particularly apt to absorbe functional programming concepts, probably due to its dynamic nature. There are popular Javascript libraries about functional programming concepts (most notably Ramda), but today I’m taking a more “back to the roots” approach in interfacing Javascript with functional programming. Let’s see where it goes.

One argument to rule them all

Photo by Jeremy Perkins on Unsplash

One of the main differences between functional languages and imperative and OO languages is how the functional ones strictly adhere to the Lambda Calculus theory. Citing Wikipedia, “Lambda calculus (also written as λ-calculus) is a formal system in mathematical logic for expressing computation based on function abstraction and application using variable binding and substitution”. The theory behind that is not extremely complex, especially if you have some experience in any programming language. In fact, we use binding and substitution everyday.

To put it simply, we are speaking of a theory where you can define functions with named arguments, and call such functions substituting the named argument in the function body with your own values.

For example, in the function

function double(x) {
 return x * 2;
}

we are, in fact, adhering to the theory.

Being a formal system as it is, the lambda calculus doesn’t define “shortcuts” like functions with multiple arguments, since you can get the same result from the single-substitution operation repeated. That’s the same as you never define the “three operands” sum or multiplication, since defining it on two operands suffice.

Nevertheless, we grew accustomed to multi-arguments functions:

function sum(a, b) {
 return a + b;
}

What if we decide to strictly adhere to the formal theory? Can we express the same function only using single argument functions? The Lambda Calculus prove it to be possible, and in Javascript it looks like this:

function lsum(a) {
 return function(b) {
 return a + b;
 }
}

lsum defines a function which takes one argument and returns another function, with the supplied arguments already “included” (bound).

I can hear you say: “How much boilerplate code is it needed to consume functions defined like this?”

You be the judge:

lsum(2)(3)

As you can see, adhering to the rule “only one argument per function” is pushing us to a Javascript syntax which is not too bad.

Semantically, we are still working with a two arguments functions — we only have to redefine the syntax to give multiple arguments to functions — but under the hood we are adhering to the rule.

The ramifications of such a rule are wider than it may appear at first. For example, using functions that take only one arguments automagically gives the partial-application “feature” to our functions, thanks to the currying we operated.

What is currying?

Photo by Norbert Levajsics on Unsplash

Currying is the operation of taking a multi-argument function and trasforming it into multiple single-argument nested function, just like we did before.

Automatic currying is the feature in functional languages where you can create a “partial application” of a function by invoking any multi argument function with less than their total number of arguments. In our example, since we “manually” curried the lsum function, if we define

var sum2 = lsum(2)

we get a new function, sum2, which takes only one argument (the remaining one).

What really is missing at this point is an elegant way to define functions like lsum, without the overhead of multiple functions nesting. This is where some macro support would come handy in Javascript.

The “conservative” approach

Photo by Hunters Race on Unsplash

A whole different approach to partial application is taking a multi argument function and “take away” one argument at a time, to get a similar result. I call it “conservative” since it relies in the traditional semantic of the Javascript language. More on this later. We can define such a function:

function partialOne(fn, ...args) {
 return function(x) {
 return fn(...args, x)
 }
}

and use it supplying all the arguments expect the last:

function sum4(a, b, c, d) {
 return a + b + c + d;
}

var sum6to = partialOne(sum4, 1, 2, 3);

var eight = sum6to(2)

Another interesting version of partial is the following, using any number of arguments and returning a function still capable of taking all the remaining arguments.

function partial(fn, ...args) {
 return function(x) {
 return fn(...args, ...arguments);
 }
}

var sum3to = partial(sum4, 1, 2)

var ten = sum3to(3,4)

This is more or less how the partial function works in Clojure. It’s remarkable that the spread operator permits a definition which is even more terse than the one you find in the Clojure source code, and works for any number of arguments supplied to partial.

This could be useful for sure, but at this point we’re breaking the one argument rule. From now on, I will abandon this line of reasoning and stick to the “single argument” rule.

The “lambda semantic” approach

Photo by Kevin LEE on Unsplash

To recap: firstly we have defined single arguments functions and saw how this gives us partial application “for free”, then we defined helper functions to operate partial applications on existing multi arguments functions. Sadly, those two approaches are orthogonal, and that’s beacuse they assign different semantics to the way we define functions and their arguments.

This is an insight I had a while back studying F#. The syntax for F# to define a multi argument function is this:

let lsum a b = a + b

This syntax conceptually translates to the “nested functions” version described at the beginning, and could in fact be written in the same mode:

let lsum = fun b -\>
 fun a ->
 a + b

The first syntax is just syntactic sugar around the second version. For this reason, both the definitions use exclusively one argument functions under the hood.

Our F# lsum function supports partial application, but so does our Javascript version! This is important: partial application of functions is not a feature of a language, it is the inevitable byproduct of defining exclusively single argument functions, and using nested functions to define “higher order” multi arguments functions.

On the other hand, in F# we can still define a function like this:

let sumTuple (a,b) = a + b

This may seem odd and familiar at the same time. In this version we’re defining a single argument function, taking a tuple (a,b) as single argument. Tuples are a bit like array or objects, they are atomic structure containing multiple values.

This version, obviously, doesn’t allow partial application in respect to a or b, and again, neither does our “normal” multi arguments sum Javascript function!

You may see where I’m going. We could apply the same semantic for Javascript and get the exact same results we get from a functional language like F#, if we consider the Javascript usual syntax a way to define a tuple to be bound to the function. Here’s what we get:

F#:
let lsum a b = a + b

equals to

F#:
let lsum =
 fun b ->
 fun a ->
 a + b

and translates to

JS:
function lsum(a) {
 return function(b) {
 return a + b
 }
}

or better:

JS:
let lsum =
 (b) =>
 (a) => a + b

which is almost the same syntax as the second F# version.

Going further,

F#:
let sumTuple(a,b) = a + b

translates to

JS:
function sumTuple(a,b) {
 return a + b
}

Let’s compare now how you consume these functions.

F#:
let n = lsum 4 5

JS:
let n = lsum(4)(5)

and

F#:
let m = sumTuple(4,5)

JS:
let m = sumTuple(4,5)

The last two is not a typo: they’re exactly the same.

There’s more: since in F# tuples are the basic structure to contain informations — meaning that “value” is syntactic sugar for “(value)” — , we can rewrite the lsum application in F#:

F#:
let n = lsum(4)(5)

that, again, is exactly the same as the Javascript version.

Coming full circle

“A person walking through a maze made out of rocks by the side of the beach” by Ashley Batz on Unsplash

At the end of the road, what’s really missing in Javascript is:

  • a simpler way to define nested functions
  • tuples as first-class citizens of the language If you can work around these deficiencies, you can apply this “alternative” semantic — let’s call it “lambda semantic” — to Javascript and gain the advantages brought by the lambda calculus, such as partial application. “Modern” Javascript helps to overcome the first limitation. It would be great for this “lambda semantic” to be acknowledged so to have better language support. Do you think it’s something worth to consider for transpiling? Do you know existing solutions? Let me know in the comments.

Top comments (0)

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post