The Y combinator is one of the most interesting phenomenons in the lambda calculus. I doubt that right away seeing it, one can come up with an interpretation of it's functionality.
Y = f => (g => g(g))(g => n => f(g(g))(n));
The idea is to run a lambda (anonymous function) recursively.
Hey wait a minute..! How exactly you can do it if you don't have a name to reference a function and call it within itself in the first place..?
Let's try to understand it's derivation step by step. I will use the arrow functions so if you are not familiar with them please follow this link. They are very simple. x => x
means function(x){return x;}
. The JS this
keyword has a different meaning within the arrows but that's off topic as per this subject.
So as always we will go with the factorial function but the Y combinator that we will derive will be valid for all recursive functions.
A factorial function can simply be expressed as follows
var fa = n => n === 0 ? 1 : n*fa(n-1);
fa(5) // <- 120
But say that we don't want to refer to the fa
function recursively; instead we would like to derive a working factorial function from a hypothetical version of the factorial function. What's a hypothetical factorial function? The hypothetical factorial function takes a proper factorial function and returns us a working factorial function. Like the following
var fh = f => n => n === 0 ? 1 : n*f(n-1);
So if i pass fa
function to fh
as an argument it shall work. Like;
fh(fa)(5); // <- 120
But we don't want to refer to another factorial function like fa
since we have already "sort of" defined the factorial logic within the fh
function. Then we think. fh
keeps the f
argument in closure and returns me a working factorial function (n => n === 0 ? 1 : n*f(n-1)
) if i pass a proper factorial function to it like fa
. So what if i pass itself to it; A quick try fh(fh)(5) // <- NaN
meh..!
So we start playing with the inner function. Normally i would pass this step but it might be helpful to see the transformations... so lets continue. I can define the fb
function to return me a function which takes two arguments, itself and factorial count n
fb = (f,n) => n === 0 ? 1 : n* f(f,n-1), // fb(fb,5) <- 120
So far so good but the two argument factorial function is no where close to what i am looking for. I can separate them by adding another functional step known as partial application.
fc = f => n => n === 0 ? 1 : n* f(f)(n-1), // fc(fc)(5) <- 120
Now this is very close to our hypothetical function fh
. But the inner shows f(f)(n-1)
We have to correct this to show f(n-1). Well may be we can use a JS beauty IIFE to help us...
fd = f => n => ((g,n) => n === 0 ? 1 : n * g(n-1))(f(f),n) // fd(fd)(5) <- 120
Can you see the IIFE..? ((g,n) => n === 0 ? 1 : n * g(n-1))(f(f),n)
Yet, while this seems OK i have to get rid of the dual argument (g,n)
IIFE to achieve the desired result. This will take another level of getting functional by partial application.
fe = f => n => (g => n => n === 0 ? 1 : n * g(n-1))(f(f))(n) // fe(fe)(5) <- 120
Now we have inside g => n => n === 0 ? 1 : n * g(n-1)
which is the body of our hypothetical function fh
. Which means i can substitute (I love this part.. just like calculus substitution; in fact it is...) fh
in the above function to read like;
fe = f => n => fh(f(f))(n) // fe(fe)(5) <- 120
Ok time to wrap it up. What i have wanted in the first place..? I want to feed fh
to a function (Y-combinator) and get it's fixed point. Here i know that fe(fe)
uses fh
and returns me the properly working factorial function. So lets define a function to take our hyporthetical recursive function as an argument and give us something working (fixed). IIFE to help again.
Y = f => (g => g(g))(g => n => f(g(g))(n));
So this should work for anything. Let's try our Y combinator with a hypothetical Fibonacci function.
var Y = f => (g => g(g))(g => n => f(g(g))(n)),
fibH = f => n => n === 0 ? 0
: n === 1 ? 1
: f(n-2) + f(n-1),
fibo = Y(fibH);
console.log(fibo(10));
I hope it's all clear...
factorial
, it's not used any more) – OvermanY(F)
, the recursive structurex(x)
is only a reference that get passed in and stored as the bound variablefactorial
insideFactorial
function, it doesn't get called at that point. – Ambala