I had some trouble mapping some of these implementations to the
Y-combinator definition given in the question. The obvious translation
to Perl looped. Then I discovered, with the help of the Wikipedia article on fixed
point combinators, that this definition assumes call-by-name or
lazy evaluation. For call-by-value languages, such as Scheme, C, and
Perl, you need an extra layer of lambda to make it work. The Wikipedia
article calls this fixed point combinator Z.
Y = λf. (λx. f (x x)) (λx. f (x x))
Z = λf. (λx. f (λy. x x y)) (λx. f (λy. x x y))
Here is an implementation in Perl.
# Y-combinator, implemented as Z above since Perl is call by value.
$Y = sub { my ($f) = @_;
# g is shorthand for the repeated lambda x function in Z
my $g = sub { my ($x) = @_;
$f->(sub { my ($y) = @_;
$x->($x)->($y) }) };
$g->($g) };
# Factorial function for use with Y-combinator
$F = sub { my ($fact) = @_;
sub { my ($n) = @_;
$n <= 0 ? 1 : $n * $fact->($n - 1) } };
printf "%d! => %d\n", 5, $Y->($F)->(5);
This isn't too hard to explain. The call $Y->($F) produces the factorial function, which comes from Y's call to $g passing itself as the argument, and inside $g, $x is bound to $g as well, so $x->($x) is the same as $g->($g) and $Y->($F). The factorial function comes from the lambda n returned by $F, with $fact properly bound. This result is also returned by $g inside $Y and then returned by $Y itself. Finally, how is $fact, referenced by the lambda n factorial function, properly bound? That is the lambda y, not the factorial function exactly, but one that uses $x->($x) to get factorial and returns the result of passing factorial whatever it is passed.