2 ms·
Named recursion requires a notion of time, which is clear from your distinction between "permanent" and "temporary" names. This can add unnecessary complexity.
by chriswarbo 12y ago
Named recursion requires a notion of time, which is clear from your distinction between "permanent" and "temporary" names. This can add unnecessary complexity.
Let's use an analogy: is it ever useful to call a function with another function's return value?
p = \q. f (g q)
We can implement the same thing using named arguments with permanent or temporary names, for example:
p = \q. let g_of_q = g q
in f g_of_q
That forces us to write the same boilerplate every time. We can do better by encapsulating it in a function (DRY):
compose = \a. \b. \c. let b_of_c = b c
in a b_of_c
p = compose f g
The Y/Z combinators do exactly the same job as compose, except for self-application rather than chained application. They let us write down what we mean, directly, without having to break it up into artificial chunks and chain them back together. Notice that the final definition of "p" doesn't even need a lambda abstraction, since the function is being calculated automatically rather than defined manually.
For example, I've used the Z combinator when defining arrays of functions in PHP. Without it, I'd have to define the functions separately, using brand-new function abstractions to inherit the self-reference (which is horribly verbose in PHP), then combine them into an array; or I'd have to define them separately, using brand-new function abstractions to inherit the array, then combine them into the array. Either way, it would add a bunch of complexity and boilerplate.