Y combinator is one of the most interesting phenomena in lambda calculus. I doubt that once you see it, you can come up with an interpretation of its functionality.
Y = f => (g => g(g))(g => n => f(g(g))(n));
Try to understand step by step. I will use the arrow functions, so if you are not familiar with them, follow this link . They are very simple. x => x means function(x){return x;} . The JS this has different meanings within the arrows, but it does not correspond to the topic according to this subject.
So, as always, we will go with the factorial function, but the Y combinator that we deduce will be valid for all recursive functions.
The factorial function can simply be expressed as follows:
var fa = n => n === 0 ? 1 : n*fa(n-1); fact(5)
But letβs say that we donβt want to refer to the fa function recursively; instead, we would like to get a working factorial function from a hypothetical version of a factorial function. What is a hypothetical factor function? A hypothetical factorial function takes the correct factorial function and returns us the actual factorial function. As below
var fh = f => n => n === 0 ? 1 : n*f(n-1);
So, if I pass fa function fh as an argument, it will work. How;
fh(fa)(5);
But we do not want to refer to another factorial function, such as fa , since we have already defined the factorial logic in the fh function. Then we think. fh stores the argument f in closure and returns me the actual factorial function ( n => n === 0 ? 1 : n*f(n-1) ) if I pass it the corresponding factorial function, for example fa . So if I go to him; Quick try fh(fh)(5) // <- NaN meh ..!
So, we begin to play with the internal function. Normally I would go through this step, but it would be useful to see the transformations ... so let's continue. I can define a fb function to return to me a function that takes two arguments, itself and a factor counter n
fb = (f,n) => n === 0 ? 1 : n* f(f,n-1), // fb(fb,5) <- 120
So far so good, but the two argument functions have not been close to what I'm looking for. I can separate them by adding another functional step.
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 internal manifestations of f(f)(n-1) We must correct this to show f (n-1). Well, maybe we can use JS beauty IIFE to help us ...
fd => f => n => ((g,n) => n === 0 ? 1 : n * (g,n-1))(f(f),n)
Do you see IIFE ..? ((g,n) => n === 0 ? 1 : n * g(n-1))(f(f),n) However, while this seems OK, I have to get rid of the double argument (g,n) IIFE to achieve the desired result. This will take another level of functionality.
fe => f => n => (g => n => n === 0 ? 1 : n * g(n-1))(f(f))(n)
Now we have inside g => n => n === 0 ? 1 : n * g(n-1) g => n => n === 0 ? 1 : n * g(n-1) , which is the body of our hypothetical function fh . This means that I can replace (I like this part ... just like substitution based on calculus, actually it is ...) fh in the above function, for example:
fe => f => n => fh(f(f))(n)
Time to complete it. What did I want first of all ..? I want to pass fh function (Y-combinator) and get its fixed point. Here I know that fe(fe) uses fh and returns me a valid factorial function. So, let's define a function to take our hyperactive 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 everything is clear ...