Y combinator in small steps
by ciju

The Y combinator is an interesting construct, allowing recursion without referring the function being recursed. In other words, call a function recursively, without naming it. We will go through one approach of deriving it.

This approach mostly follows WhyOfY by Richard P Gabriel 1. We will derive it in JavaScript, in steps. Each step is complete. Click on the ‘Edit in JSFiddle’ to explore the steps.

Premise & setup

Lets pick a simple example, so we can concentrate on exploring recursion. Given a number, n, return the sum of numbers from 1 to n. To start with, below is a recursive solution for the problem. Individual steps are hosted in JSFiddle, for you to play around.

The process

We have to remove the reference to sum, inside the function, which fortunately leads to the Y combinator. One approach to calling a function, without using its name, is to pass the function as parameter.

We have just moved the function name (reference) from inside the function, to the function call. As a next step, we could pass the function as a curried param.

This helps later, in abstracting the passing of function as parameter, out of the logic for the problem.

The double call inside the inner function, is quite irrelevant to the problem of calculating sum. The logic of sum shouldn’t know that h(h) gives the next function call. The recursive function should just receive a function, to continue the recursion with. We could abstract that in another function. Ex:

// replace
return function (n) {
    return n === 1 ? 1 : n + h(h)(n-1);
};

// with
return (function(f) {
    return function (n) {
        return n === 1 ? 1 : n + f(n-1);
    };
})(function (n) {
    return h(h)(n);
});

Why not pass h(h) as parameter? Why do we have to wrap it in a function?

Here is the complete version.

In the current version, the function dealing with the sum logic, doesn’t have any dependency on context, or specific knowledge about how to get the next function. Lets name it as sum (for the time being). Ex.

var sum = function(f) {
    return function (n) {
        return n === 1 ? 1 : n + f(n-1);
    };
};

This is just so we can see the real changes in code. Here is the recursion with sum.

To evaluate the recursion, we had to call s4 with itself, ex s4(s4). To not use a name, we would have to abstract it in a function. We can define a function which s4 as parameter, and call it with itself as parameter.

var s5 = (function (f) {
    return f(f);
})(s4);
console.assert(s5(5) === 15);

Expanding the definition of s4 gives.

We are almost done. Now, we just need to pass the actual function (ex sum in our case) as a parameter. Here is the Y combinator.

Or in its inlined form:

Explorations

If you would like to think more about this, here are a few thoughts to explore.

  1. We have only explored function with single argument. How could we make this work with multiple arguments.

  2. Could we come up with a combinator, which doesn’t take curried version of sum. Ex a combinator which takes sum defined as function sum(f, n) { return n + f(n-1); } as argument.

PS: ActiveSphere is hiring. If we can interest you, please visit our careers page.

1: A somewhat similar approach is followed by James Coglan’s blog post. And watch an interesting talk on it, by Jim Weirich.

⇐ Visualization of backoff functions   |   Mocha TDD like DSL in less than 100 lines ⇒