# Y Combinator and Trampolines in Javascript

01 Sep 2019In *Why Y? Deriving the Y Combinator in JavaScript*, Reginald
Braithwaithe (aka Raganwald) explains, step by step, how the fixed-point Y
combinator and the long-tailed widowbird can be implemented in Javascript.

At this point, you're probably wondering **What the hell are we talking about?**

Essentially, these are tools that we can use to achieve two very useful things:

- Inject code at each recursive call in a recursive function.
- Make a tail-recursive function use a fixed amount of stack space, preventing the fabled stack overflow.

Sometimes, achieving the second item is called *using a trampoline*, because
we enter the recursive function but on "*recursion*", we jump right back out.

The technique is also called "tail-recursion elimination" or "tail call optimization".

In this post I want to offer a very condensed version of this story that focuses on the results. A motivation, really. You should read Reginald's article to know about the gory details.

I also simplified the combinators' implementation — to make them more idiomatic, and, in my opinion, easier to understand.

## The Mockingbird (Code Injection)

Let's start with a recursive (JavaScript) function:

```
const is_even = n =>
(n === 0) || !is_even(n - 1);
```

This implement a very inefficient way to check if a number is even. It's a toy example, obviously.

Let's take our first goal first: "inject code at each recursive call in a recursive function".

To achieve this, we need to rewrite the function in a different form. Here is the so-called "mockingbird form":

```
const mock_is_even = (myself, n) =>
(n === 0) || !myself(myself, n - 1);
```

`is_even`

now take a `myself`

function as argument, which it called recursively,
passing itself along.

We can call `mock_is_even(mock_is_even, 10)`

, and it will behave just like
`is_even(10)`

. So far that's pretty useless.

We can re-implement `is_even`

in terms of `mock_is_even`

using the "mockingbird"
combinator:

```
const mockingbird =
fn =>
(...args) =>
fn(fn, ...args);
const is_even = mockingbird(mock_is_even);
```

Okay, that works: when we call `is_even(10)`

, the call becomes
`mock_is_even(mock_is_even, 10)`

, exactly what we wanted.

What's the point?

Well, now we can inject code. Assume we want to memoize the function. After all,
the result of `is_even(X)`

will always be the same for a given `X`

.

```
const memoized = fn => {
const map = new Map();
return (...args) => {
const key = JSON.stringify(args);
return map[key] || (map[key] = fn(args));
}
}
const is_even_memo = mockingbird(memoized(mock_is_even));
```

We can verify it's working by measuring the run time:

```
console.time('slow');
is_even_memo(100); // not memoized yet
console.timeEnd('slow');
console.time('fast');
is_even_memo(100); // memoized
console.timeEnd('fast');
```

## The Y Combinator (Better Code Injection)

This `myself(myself, ...)`

business is ugly. Can we do better? Yes.

Instead of the mockingbird form:

```
const mock_is_even = (myself, n) =>
(n === 0) || !myself(myself, n - 1);
```

We can write our function in Y form:

```
const Y_is_even = (myself, n) =>
(n === 0) || !myself(n - 1);
```

And here is how we derive the `is_even`

from the Y combinator:

```
const Y = fn => {
const wrapper = (...args) => fn(wrapper, ...args);
return wrapper;
}
const is_even = Y(Y_is_even);
```

Injection is done the same way as before:

`const is_even_memo = Y(memoized(Y_is_even));`

Let's explain: the Y combinator is similar to the mockingbird, but takes care of passing the function to itself, so that we don't need to do it in Y-form functions, unlike in mockingbird-form.

Reginald's post provides the traditional way to express the combinator, using only function parameters, whereas we "cheat" by using a variable inside the function. If you want to understand the Y combinator's root in lambda calculus, give the post a read!

## The Longtailed Widowbird

(Tail Recursion Elimination / Trampolines)

Let's move on to our second goal: "Make a tail-recursive function use a fixed amount of stack space, preventing the fabled stack overflow."

This process will work with functions written in Y-form — but only as long as
they are *tail-recursive*.

Our `Y_is_even`

function is not tail recursive, pecause of that pesky `!`

operator, which is applied after the call to `myself`

returns.

Fortunately, we can rewrite it to be tail-recursive:

```
const Y_is_even_tailrec = (myself, n) =>
n === 0 ? true
n === 1 ? false
: myself(n - 2);
```

The idea behind the longtailed widowbird is that, instead of injecting code
around the recursive function call, we will *replace* the recursive call by code
that returns a function (which executes the "recursive" function call).

Once we get this function, we can call it. If it does another recursive call, it will return another function, which we can call as well. And so on and so forth. We call the returned functions in a loop, until we get the final result. Recursion has been eliminated in favor of iteration!

Here is the longtailed widowbird combinator:

```
const longtailed = fn => (...args0) => {
class Thunk {
constructor (delayed) {
this.delayed = delayed;
}
evaluate() {
return this.delayed();
}
}
const wrapper = (...args) =>
new Thunk(() => fn(wrapper, ...args));
let value = fn(wrapper, ...args0);
while (value instanceof Thunk)
value = value.evaluate();
return value;
}
```

Let's go over this.

The `Thunk`

class is just a way to identify the functions we need to execute in
the loop (it's used in the `value instancceof Thunk`

test). If we didn't have
this class and just tested for JavaScript's `Function`

class, our combinator
wouldn't work with recursive functions that return functions!

We do the same `wrapper`

trick as in the Y combinator, but this time we also
wrap the call in a function we store in a `Thunk`

instance.

Then we get the initial value and we start iterating, until a result is obtained!

Let's try this!

```
const iter_is_even = longtailed(Y_is_even_tailrec);
// No problem!
iter_is_even(1000000);
// Maximum call stack size exceeded
is_even(1000000);
```

Can we still memoize? Of course:

`const iter_is_even_memo = longtailed(memoized(Y_is_even_tailrec));`

And that's it folks! Code injection in recursive functions, and tail call optimization in JavaScript, easy as pie!

You can find all the code in this article collected in this gist.