Fixed-point combinator: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
typo
Updating U Saarland link to correct URL
Tags: Reverted Disambiguation links added
Line 1: Line 1:
{{Technical|date=December 2011}}
{{Short description|1=Higher-order function Y for which Y f = f (Y f)}}
In [[computer science]], a '''fixed-point combinator''' (or '''fixpoint combinator<ref>{{cite book|last=Peyton Jones|first=Simon L.|title=The Implementation of Functional Programming|year=1987|publisher=Prentice Hall International|url=http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/}}</ref>''') is a [[higher-order function]] that computes a [[fixed point (mathematics)|fixed point]] of other functions. A ''fixed point'' of a function '''f''' is a value that '''f''' doesn't change (''x'' such that ''x'' = '''f'''(''x'') ).
In mathematics and [[computer science]] in general, a ''[[fixed point (mathematics)|fixed point]]'' of a function is a value that is mapped to itself by the function.


Consider the function '''f'''(''x'') = x<sup>2</sup>. 0 and 1 are fixed points of this function, because 0 = 0<sup>2</sup> and 1 = 1<sup>2</sup>. This function has no other fixed points.
In [[combinatory logic]] for [[computer science]], a '''fixed-point combinator''' (or '''fixpoint combinator''')<ref name="SPJ1987">{{cite book |last=Peyton Jones |first=Simon L. |title=The Implementation of Functional Programming |year=1987 |publisher=Prentice Hall International |url=https://www.microsoft.com/en-us/research/publication/the-implementation-of-functional-programming-languages/}}</ref>{{rp|page 26}} is a [[higher-order function]] <math>\textsf{fix}</math> that returns some fixed point of its argument function, if one exists.


A very different example comes up with many familiar [[markup language]]s. For example, [[HTML]] ''list'' elements contain ''item'' elements which contain paragraphs (''p'' elements) which contain text, emphasis elements like ''b'' and ''i'', and so on (this amounts to a [[tree|Tree_(data_structure)]]). Given a set of elements, it is often useful also to retrieve all of their descendents. One way is to define a simpler function (call it '''f''') that takes a set of elements, and returns a set that contains all of those elements '''plus''' all of their (direct) child elements. For example, if ''x'' is a set of 3 paragraphs, '''f'''(''x'') includes those 3 paragraphs and all of the emphasis elements, text, and other they directly contain. If you use '''f''' again ('''f'''('''f'''(''x''))), you add all of x's grandchild elements. Repeating eventually accumulates all of the descendants. Then evaluating '''f''' again can never add any more elements. This is a fixed-point for '''f''', because the set returned by '''f''' is the same as the set passed to '''f'''. Thus, a function '''g''' that applies '''f''' repeatedly and returns the set of original elements and all their descendants, is a fixed-point combinator.
Formally, if the function ''f'' has one or more fixed points, then
: <math>\textsf{fix}\ f = f\ (\textsf{fix}\ f)\ ,</math>


The [[Recursive join]] in [[relational databases]] is very similar.
and hence, by repeated application,
: <math>\textsf{fix}\ f = f\ (f\ ( \ldots f\ (\textsf{fix}\ f) \ldots))\ .</math>


The function for which any input is a fixed point is called the [[Identity function]]. Other functions have the special property that after being applied once, further applications don't have any effect. More formally: '''f'''('''f'''(''x'')) = '''f'''(''x'') for all ''x''. Such functions are called [[idempotent]]. An example of such a function is the function that returns ''0'' for all even integers, and ''1'' for all odd integers.
==Y combinator==
In the classical untyped [[lambda calculus]], every function has a fixed point.


When '''f''' is a first-order function (a function on "simple" values such as integers), a fixed point is a first-order value. However, the notion also applies to [[First-class_function#Higher-order_functions:_passing_functions_as_arguments|higher-order functions]]: '''f''' takes ''another function'' '''p''' such that '''p''' = '''f'''('''p'''). A fixed-point combinator, then, is a function '''g''' which produces such a fixed point '''p''' for any function '''f''':
A particular implementation of '''fix''' is [[Haskell Curry|Curry's]] paradoxical combinator '''Y''', represented by
: '''g'''('''f''') = '''p''', where '''p''' = '''f'''('''p''')
: <math>\textsf{Y} = \lambda f. \ (\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))\ .</math><ref name="Barendregt.1985">{{cite book |author=Henk Barendregt |author-link=Henk Barendregt |title=The Lambda Calculus &ndash; Its Syntax and Semantics |location=Amsterdam |publisher=North-Holland |series=Studies in Logic and the Foundations of Mathematics |volume=103 |year=1985 |isbn=0444867481}}</ref>{{rp|131}}<ref group=note>Throughout this article, the syntax rules given in [[Lambda calculus#Notation]] are used, to save parentheses.</ref><ref group="note" name="Y demonstration">For an arbitrary lambda term ''f'', the fixed-point property can be validated by beta reducing the left- and the right-hand side:
or, alternatively:
{{nowrap|<math>\textsf{Y} \ f</math>}}
: '''g'''('''f''') = '''f'''('''g'''('''f''')).
{{nowrap|<math>\equiv</math>}}
{{nowrap|<math>( \lambda f.(\lambda x.f (x x)) \ (\lambda x.f (x x)) ) \ f</math>}}
{{nowrap|<math>\to</math>}}
{{nowrap|<math>(\lambda x.f (x x)) \ (\lambda x.f (x x))</math>}}
{{nowrap|<math>\to</math>}}
{{nowrap|<math>f \ ( (\lambda x.f (x x)) \ (\lambda x.f (x x)) )</math>,}}
where <math>\equiv</math> and <math>\to</math> denote syntactic equality by definition and beta reduction, respectively.


Because fixed-point combinators are higher-order functions, their history is intimately related to the development of [[lambda calculus]]. One well-known fixed-point combinator in the [[untyped lambda calculus]] is [[Haskell Curry]]'s '''Y''' = λf·(λx·f (x x)) (λx·f (x x)). The name of this combinator is sometimes incorrectly used to refer to any fixed-point combinator. The untyped lambda calculus however, contains an infinitude of fixed-point combinators.<ref name="bimbo">{{cite book|last=Bimbó|first=Katalin|title=Combinatory Logic: Pure, Applied and Typed|page=48|year=}}</ref> Fixed-point combinators do not necessarily exist in more restrictive models of computation. For instance, they do not exist in [[simply typed lambda calculus]].
Similarly to the first two steps, one obtains
{{nowrap|<math>f\ (\textsf{Y}\ f)</math>}}
{{nowrap|<math>\to</math>}}
{{nowrap|<math>f\ ( (\lambda x.f (x x))\ (\lambda x.f (x x)) )</math>.}}
Since both terms <math>\textsf{Y}\ f</math> and <math>f\ (\textsf{Y}\ f)</math> could be reduced to the same term, they are equal.</ref>


In programming languages that support [[anonymous function]]s, fixed-point combinators allow the definition and use of anonymous [[recursion|recursive functions]], i.e. without having to [[Name binding|bind]] such functions to [[identifier]]s. In this setting, the use of fixed-point combinators is sometimes called '''anonymous recursion'''.<ref>This terminology appear to be largely [[mathematical folklore|folklore]], but it does appear in the following:
In [[functional programming]], the Y combinator can be used to formally define [[recursion (computer science)|recursive function]]s in a programming language that does not support recursion.
* Trey Nash, ''Accelerated C# 2008'', Apress, 2007, ISBN 1-59059-873-3, p. 462—463. Derived substantially<!--gives him credit!--> from [http://www.linkedin.com/pub/wes-dyer/2/727/a39 Wes Dyer]'s blog (see next item).
* Wes Dyer [http://blogs.msdn.com/wesdyer/archive/2007/02/02/anonymous-recursion-in-c.aspx Anonymous Recursion in C#], February 02, 2007, contains a substantially similar example found in the book above, but accompanied by more discussion.</ref><ref name=ifworks>The If Works [http://blog.jcoglan.com/2008/01/10/deriving-the-y-combinator/ Deriving the Y combinator], January 10th, 2008</ref>


==How it works==
This combinator may be used in implementing [[Curry's paradox]]. The heart of Curry's paradox is that untyped lambda calculus is unsound as a deductive system, and the '''Y''' combinator demonstrates that by allowing an anonymous expression to represent zero, or even many values. This is inconsistent in mathematical logic.
Ignoring for now the question whether fixed-point combinators even exist (to be addressed in the next section), we illustrate how a function satisfying the fixed-point combinator equation works. To trace the computation steps, we choose [[untyped lambda calculus]] as our programming language. The (computational) equality from the equation that defines a fixed-point combinator corresponds to [[beta reduction]] in lambda calculus.


As a concrete example of a fixed-point combinator applied to a function, we use the standard recursive mathematical equation that defines the [[factorial function]]
Applied to a function with one variable the '''Y''' combinator usually does not terminate. More interesting results are obtained by applying the '''Y''' combinator to functions of two or more variables. The second variable may be used as a counter, or index. The resulting function behaves like a ''while'' or a ''for'' loop in an imperative language.


:'''fact'''(''n'') = if ''n'' = 0 then 1 else ''n'' * '''fact'''(''n'' − 1)
Used in this way the '''Y''' combinator implements simple recursion. In the lambda calculus it is not possible to refer to the definition of a function in a function body. Recursion may only be achieved by passing in a function as a parameter. The '''Y''' combinator demonstrates this style of programming.


We can express a single step of this recursion in lambda calculus as the lambda abstraction
==Fixed-point combinator==
'''F''' = λf. λn. IFTHENELSE (ISZERO n) 1 (MULT n (f (PRED n)))


using the usual encoding for booleans, and [[Church encoding]] for numerals. The functions in CAPITALS above can all be defined as lambda abstractions with their intuitive meaning; see [[lambda calculus]] for their precise definitions. Intuitively, f in '''F''' is a place-holder argument for the factorial function itself.
The '''Y''' combinator is an implementation of a fixed-point combinator in lambda calculus. Fixed-point combinators may also be easily defined in other functional and imperative languages. The implementation in lambda calculus is more difficult due to limitations in lambda calculus.
The fixed-point combinator may be used in a number of different areas:
* [[Mathematics|General mathematics]]
* Untyped [[lambda calculus]]
* [[Typed lambda calculus]]
* [[Functional programming]]
* [[Imperative programming]]


We now investigate what happens when a fixed-point combinator FIX is applied to '''F''' and the resulting abstraction, which we hope would be the factorial function, is in turn applied to a (Church-encoded) numeral.
Fixed-point combinators may be applied to a range of different functions, but normally will not terminate unless there is an extra parameter. When the function to be fixed refers to its parameter, another call to the function is invoked, so the calculation never gets started. Instead, the extra parameter is used to trigger the start of the calculation.


(FIX '''F''') n = ('''F''' (FIX '''F''')) n --- expanded the defining equation of FIX
The type of the fixed point is the return type of the function being fixed. This may be a real or a function or any other type.
= (λx. IFTHENELSE (ISZERO x) 1 (MULT x ((FIX '''F''') (PRED x)))) n --- expanded the first '''F'''
= IFTHENELSE (ISZERO n) 1 (MULT n ((FIX '''F''') (PRED n))) --- applied abstraction to n.


If we now abbreviate FIX '''F''' as FACT, we recognize that any application of the abstraction FACT to a Church numeral calculates its factorial
In the untyped lambda calculus, the function to apply the fixed-point combinator to may be expressed using an encoding, like [[Church encoding]]. In this case particular lambda terms (which define functions) are considered as values. "Running" (beta reducing) the fixed-point combinator on the encoding gives a lambda term for the result which may then be interpreted as fixed-point value.
FACT n = IFTHENELSE (ISZERO n) 1 (MULT n (FACT (PRED n))).


Recall that in lambda calculus all abstractions are anonymous, and that the names we give to some of them are only [[syntactic sugar]]. Therefore, it's meaningless in this context to contrast FACT as a recursive function with F as somehow being "not recursive".<!-- do not remove this, a lot of people, including editors here make that confusion. --> What fixed point operators really buy us here is the ability to ''solve [[recursive equation]]s''. That is, we can ask the question in reverse: does there exist a lambda abstraction that satisfies the equation of FACT? The answer is yes, and we have a "mechanical" procedure for producing such an abstraction: simply define '''F''' as above, and then use any fixed point combinator FIX to obtain FACT as FIX '''F'''.
Alternately, a function may be considered as a lambda term defined purely in lambda calculus.


In the practice of programming, substituting FACT at a [[call site]] by the expression FIX '''F''' is sometimes called ''anonymous recursion''. In the lambda abstraction '''F''', FACT is represented by the [[bound variable]] f, which corresponds to an [[Argument (computer science)|argument]] in a programming language, therefore '''F''' need not be [[name binding|bound]] to an identifier. '''F''' however is a [[higher-order function]] because the argument ''f'' is itself called as a function, so the programming language must allow passing functions as arguments. It must also allow [[function literal]]s because FIX '''F''' is an [[Expression (programming)|expression]] rather than an identifier. In practice, there are further limitations imposed on FIX, depending on the [[evaluation strategy]] employed by the programming environment and the [[type checker]]. These are described in the implementation section.
These different approaches affect how a mathematician and a programmer may regard a fixed-point combinator. A lambda calculus mathematician may see the ''Y'' combinator applied to a function as being an expression satisfying the fixed-point equation, and therefore a solution.


==Existence of fixed-point combinators==<!--I don't like this as heading, but "theory of ..." sounds rather dry-->
In contrast, a person only wanting to apply a fixed-point combinator to some general programming task may see it only as a means of implementing recursion.
In certain mathematical formalizations of computation, such as the [[untyped lambda calculus]] and [[combinatory logic]], every expression can be considered a higher-order function. In these formalizations, the existence of a fixed-point combinator means that ''every function has at least one fixed point;'' a function may have more than one distinct fixed point.


In some other systems, for example in the [[simply typed lambda calculus]], a well-typed fixed-point combinator cannot be written. In those systems any support for recursion must be explicitly added to the language. In still others, such as the simply-typed lambda calculus extended with [[recursive type]]s, fixed-point operators can be written, but the type of a "useful" fixed-point operator (one whose application always returns) may be restricted. In [[polymorphic lambda calculus]] ([[System F]]) a polymorphic fixed-point combinator has type ∀a.(a→a)→a, where ''a'' is a [[type variable]].
===Values and domains===


===Y combinator===
Every expression has one value. This is true in general mathematics and it must be true in lambda calculus. This means that in lambda calculus, applying a fixed-point combinator to a function gives you an expression whose value is the fixed point of the function.
{{redirect|Y combinator|the technology venture capital firm|Y Combinator (company)}}


One well-known (and perhaps the simplest) fixed-point combinator in the [[untyped lambda calculus]] is called the '''Y''' combinator. It was discovered by [[Haskell Curry|Haskell B. Curry]], and is defined as:
However, this is a value in the [[deductive lambda calculus#Set theoretic domain|lambda calculus domain]], it may not correspond to any value in the domain of the function, so in a practical sense it is not necessarily a fixed point of the function, and only in the lambda calculus domain is it a fixed point of the equation.


: '''Y''' = λf.(λx.f (x x)) (λx.f (x x))
For example, consider
: <math>x^2 = -1 \Rightarrow x = \frac{-1}{x} \Rightarrow f\ x = \frac{-1}{x} \land \mathsf Y\ f = x</math>


We can see that this function acts as a fixed-point combinator by applying it to an example function ''g'' and rewriting:
[[Church encoding#Division|Division]] of [[Church encoding#Signed numbers|signed numbers]] may be implemented in the Church encoding, so ''f'' may be represented by a lambda term. This equation has no solution in the real numbers. But in the domain of the [[complex number]]s ''i'' and -''i'' are solutions. This demonstrates that there may be solutions to an equation in another domain. However, the lambda term for the solution for the above equation is weirder than that. The lambda term <math>\mathsf Y\ f</math> represents the state where x could be either ''i'' or -''i'', as one value. The information distinguishing these two values has been lost, in the change of domain.


For the lambda calculus mathematician, this is a consequence of the definition of lambda calculus. For the programmer, it means that the beta reduction of the lambda term will loop forever, never reaching a normal form.

===Function versus implementation===

The fixed-point combinator may be defined in mathematics and then implemented in other languages. General mathematics defines a function based on its [[extensionality|extensional]] properties.<ref>{{cite web |last=Selinger |first=Peter |title=Lecture Notes on Lambda Calculus (PDF) |url=https://www.mscs.dal.ca/~selinger/papers/#lambdanotes |pages=6}} </ref> That is, two functions are equal if they perform the same mapping. Lambda calculus and programming languages regard function identity as an [[intensional definition|intensional]] property. A function's identity is based on its implementation.

A lambda calculus function (or term) is an implementation of a mathematical function. In the lambda calculus there are a number of combinators (implementations) that satisfy the mathematical definition of a fixed-point combinator.

===What is a "combinator"?===

[[Combinatory logic]] is a [[higher-order function]]s theory. A [[combinator]] is a ''closed'' lambda expression, meaning that it has no free variables. The combinators may be combined to direct values to their correct places in the expression without ever naming them as variables.

==Usage in programming==

Fixed-point combinators can be used to implement [[recursive definition]] of functions. However, they are rarely used in practical programming.<ref>{{cite web |title=For those of us who don't know what a Y-Combinator is or why it's useful, ... |url=https://news.ycombinator.com/item?id=17953711 |website=Hacker News |access-date=2 August 2020}}</ref> [[Strongly normalizing]] [[type system]]s such as the [[simply typed lambda calculus]] disallow non-termination and hence fixed-point combinators often cannot be assigned a type or require complex type system features. Furthermore fixed-point combinators are often inefficient compared to other strategies for implementing recursion, as they require more function reductions and construct and take apart a tuple for each group of mutually recursive definitions.<ref name="SPJ1987" />{{rp|page 232}}

===The factorial function===

The factorial function provides a good example of how the fixed-point combinator may be applied. The result demonstrates simple recursion, as would be implemented in a single loop in an imperative language. The definition of numbers used is explained in [[Church encoding]].

The function taking itself as a parameter is
: <math>F\ f\ n = (\operatorname{IsZero}\ n)\ 1\ (\operatorname{multiply}\ n\ (f\ (\operatorname{pred}\ n)))\ .</math>

This gives ''Y F n'' as
: <math>\begin{align} \textsf{fix}\ F\ n
&= F\ (\textsf{fix}\ F)\ n \\
&= (\operatorname{IsZero}\ n)\ 1\ (\operatorname{multiply}\ n\ ((\textsf{fix}\ F)\ (\operatorname{pred}\ n)))\ .\end{align}</math>

Setting <math>\textsf{fix}\ F = \operatorname{fact}</math> gives
: <math>\operatorname{fact}\ n = (\operatorname{IsZero}\ n)\ 1\ (\operatorname{multiply}\ n\ (\operatorname{fact}\ (\operatorname{pred}\ n)))\ .</math>

This definition puts ''F'' in the role of the body of a loop to be iterated, and is equivalent to the mathematical definition of factorial:
: <math>\operatorname{fact}\ n = \begin{cases}
1 & \text{if} ~ n = 0 \\
n \times \operatorname{fact} \ (n - 1) & \text{otherwise.}
\end{cases}</math>

==Fixed-point combinators in lambda calculus==

The ''Y'' combinator, discovered by [[Haskell Curry|Haskell B. Curry]], is defined as
: <math>Y = \lambda f.(\lambda x.f\ (x\ x)) \ (\lambda x.f\ (x\ x))\ .</math>

[[Beta reduction]] of this gives:
{| cellpadding="0"
{| cellpadding="0"
| <math>Y\ g</math>
| '''Y''' g
| <math>= (\lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x)))\ g</math>
| = (λf . (λx . f (x x)) (λx . f (x x))) g
|style="padding-left:30px;"| (by definition of ''Y'')
|style="padding-left:30px;"| (by definition of '''Y''')
|-
|-
|
|
| <math>= (\lambda x.g\ (x\ x))\ (\lambda x.g\ (x\ x))</math>
| = (λx . g (x x)) (λx . g (x x))
|style="padding-left:30px;"| (by [[β-reduction]] of λf: applied Y to g)
|style="padding-left:30px;"| ([[β-reduction]] of λf: applied to g)
|-
|-
|
|
| <math>= g\ ((\lambda x.g\ (x\ x))\ (\lambda x.g\ (x\ x)))</math>
| = g ((λx . g (x x)) (λx . g (x x)))
|style="padding-left:30px;"| (by β-reduction of λx: applied left function to right function)
|style="padding-left:30px;"| (β-reduction of λx: applied left function to right function)
|-
|-
|
|
| <math>= g\ (Y\ g)</math>
| = g ('''Y''' g)
|style="padding-left:30px;"| (by second equality)
|style="padding-left:30px;"| (by second equality)
|}
|}


In programming practice, the above '''Y''' combinator is useful only in those languages that provide a non-strict [[evaluation strategy]], such as call-by-name, since ('''Y''' ''g'') [[Divergence (computer science)|diverges]] (for any ''g'') in call-by-value settings.
Repeatedly applying this equality gives:
: <math>Y\ g = g\ (Y\ g) = g\ (g\ (Y\ g)) = g\ (\ldots g\ (Y\ g) \ldots)</math>


For call-by-value languages, we need to eta-expand the self-applications (x x) inside the combinator, resulting in the call-by-value '''Y''' combinator (also called the '''Z''' combinator):
(The equality above should be thought of as a sequence of multi-step β-reductions from left to right. The lambda term <math>g\ (Y\ g)</math> may not, in general, β-reduce to the term <math>Y\ g</math>. One can interpret the equality signs as β-equivalences instead of multi-step β-reductions to allow for going in both directions.)


: '''Y''' = (λx.f (λv.((x x) v))) (λx.f (λv.((x x) v)))
===Equivalent definition of a fixed-point combinator===


The following examples demonstrate the use of the call-by-value '''Y''' combinator (or the '''Z''' combinator).
This fixed-point combinator may be defined as ''y'', as in
: <math>x = f\ x \land y\ f = x</math>


An expression for y may be derived using rules from the [[let expression#Let definition in mathematics|definition of a let expression]]. Firstly, using the rule
: <math>(\exists x E \land F) \iff \operatorname{let} x : E \operatorname{in} F</math>


==== Example in Scheme ====
gives
: <math>\operatorname{let} x = f\ x \operatorname{in} y\ f = x\ .</math>


<source lang="scheme">
Also, using
(define Y
: <math>x \not \in \operatorname{FV}(E) \land x \in \operatorname{FV}(F) \to \operatorname{let} x : G \operatorname{in} E\ F = E\ (\operatorname{let} x : G \operatorname{in} F)</math>
(lambda (f)
((lambda (x) (f (lambda (v) ((x x) v))))
(lambda (x) (f (lambda (v) ((x x) v)))))))
</source>


Factorial definition using '''Y Combinator'''
gives
<source lang="scheme">
: <math>y\ f = \operatorname{let} x = f\ x \operatorname{in} x\ .</math>
(define fact
(Y (lambda (f)
(lambda (n)
(if (= n 0)
1
(* n (f (- n 1))))))))
</source>


==== Example in Common Lisp ====
And then using the [[deductive lambda calculus#Eta reduction as mathematics|eta reduction]] rule,
: <math>f\ x = y \iff f = \lambda x.y\ ,</math>


<source lang="lisp">
gives
(defun Y (f)
: <math>y = \lambda f.\operatorname{let} x = f\ x \operatorname{in} x\ .</math>
((lambda (x)
(funcall f (lambda (v)
(funcall (funcall x x) v))))
(lambda (x)
(funcall f (lambda (v)
(funcall (funcall x x) v))))))
</source>


===Derivation of the Y combinator===
Factorial program using the Y combinator
<source lang="lisp">
(funcall
(Y (lambda (f)
(lambda (n)
(if (= n 1)
1
(* n (funcall f (- n 1))))))) 5)


=> 120
Curry's Y combinator may be readily obtained from the definition of ''y''.<ref>{{cite web |url=https://math.stackexchange.com/questions/51246/can-someone-explain-the-y-combinator |title=abstract algebra - Can someone explain the Y Combinator? |website=Mathematics Stack Exchange}}</ref>
</source>


==== Example in Erlang ====
Starting with,
: <math>\lambda f.\operatorname{let} x = f\ x \operatorname{in} x\ ,</math>


<source lang="erlang">
A lambda abstraction does not support reference to the variable name, in the applied expression, so ''x'' must be passed in as a parameter to ''x''. We can think of this as replacing ''x'' by ''x x'', but formally this is not correct. Instead defining ''y'' by <math>\forall z, y\ z = x</math> gives
-module(y_comb).
: <math>\lambda f.\operatorname{let} y\ z = f\ (y\ z) \operatorname{in} y\ z\ .</math>
-export([y2/1]).


y2(F) ->
The let expression may be regarded as the definition of the function ''y'', where ''z'' is the parameter. Instantiation ''z'' as ''y'' in the call gives
(fun (G) -> G(G) end)(
: <math>\lambda f.\operatorname{let} y\ z = f\ (y\ z) \operatorname{in} y\ y\ .</math>
fun (G) ->
F(fun (X, Y) -> (G(G))(X, Y) end)
end
).
</source>


List reversing using the Y combinator
And, because the parameter ''z'' always passes the function ''y'',
<source lang="erlang">
: <math>\lambda f.\operatorname{let} y\ z = f\ (z\ z) \operatorname{in} y\ y\ .</math>


(y_comb:y2(
Using the [[deductive lambda calculus#Eta reduction as mathematics|eta reduction]] rule,
fun (R) ->
: <math>f\ x = y \equiv f = \lambda x.y\ ,</math>
fun
([H|T], L) -> R(T, [H|L]);
([], L) -> L
end
end
))(" eoJ", "Armstrong").


=> "Joe Armstrong"
gives
</source>
: <math>\lambda f.\operatorname{let} y = \lambda z.f\ (z\ z) \operatorname{in} y\ y\ .</math>


==== Example in JavaScript ====
A [[let expression#Let definition in mathematics|let expression may be expressed as a lambda abstraction]]; using
Y combinator function<ref>Douglas Crockford, The Little JavaScripter: [http://www.crockford.com/javascript/little.html]</ref>
: <math>n \not \in FV(E) \to (\operatorname{let} n = E \operatorname{in} L \equiv (\lambda n.L)\ E)</math>
<source lang="javascript">
function Y(f) {
return
((function (x) {
return f(function (v) { return x(x)(v); }); })
(function (x) {
return f(function (v) { return x(x)(v); }); })
);
}
</source>


Factorial function using the Y combinator
gives
<source lang="javascript">
: <math>\lambda f.(\lambda y.y\ y)\ (\lambda z.f\ (z\ z))\ .</math>
var factorial = Y(function (fac) {
return function (n) {
if (n == 0) { return 1; }
else { return n * fac(n - 1); }
};
});


factorial(5);
This is possibly the simplest implementation of a fixed-point combinator in lambda calculus. However, one beta reduction gives the more symmetrical form of Curry's Y combinator:
: <math>\lambda f.(\lambda z.f\ (z\ z))\ (\lambda z.f\ (z\ z))\ .</math>


==> 120
See also [[Let expression#Conversion from let to lambda expressions|Translating between let and lambda expressions]].


</source>
===Other fixed-point combinators===


In untyped lambda calculus fixed-point combinators are not especially rare. In fact there are infinitely many of them.<ref name="bimbo">{{cite book |last=Bimbó |first=Katalin |author-link= Katalin Bimbó |title=Combinatory Logic: Pure, Applied and Typed |date=27 July 2011 |page=48 |isbn=9781439800010 |url=https://books.google.com/books?id=iQjMBQAAQBAJ&q=%22fixed-point+combinator%22}}</ref> In 2005 Mayer Goldberg showed that the set of fixed-point combinators of untyped lambda calculus is [[recursively enumerable]].<ref name=gold>Goldberg, 2005</ref>


==== Example in Python ====
The ''Y'' combinator can be expressed in the [[SKI combinator calculus|SKI-calculus]] as
: <math>Y = S (K (S I I)) (S (S (K S) K) (K (S I I)))\ .</math>


<source lang="python">
The simplest fixed-point combinator in the SK-calculus, found by [[John Tromp]], is
def Y(f):
: <math>Y' = S S K (S (K (S S (S (S S K)))) K)\ .</math>
return (lambda x: f(lambda v: x(x)(v))) (lambda x: f(lambda v: x(x)(v)))


def fact(f):
although note that it is not in normal form, which is longer. This combinator corresponds to the lambda expression
: <math>Y' = (\lambda x y. x y x) (\lambda y x. y (x y x))\ .</math>
return (lambda n: 1 if (n == 0) else n * f(n - 1))
</source>
Alternatively, with further use of lambda abstractions
<source lang="python">
Y = (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))


fact = (lambda f: (lambda n: 1 if (n == 0) else n * f(n - 1)))
The following fixed-point combinator is simpler than the Y combinator, and β-reduces into the Y combinator; it is sometimes cited as the Y combinator itself:
: <math>X = \lambda f.(\lambda x.x x) (\lambda x.f (x x))\ .</math>


Y(fact)(5)
Another common fixed-point combinator is the Turing fixed-point combinator (named after its discoverer, [[Alan Turing]]):<ref>{{cite journal |jstor=2268281 |author=Alan Mathison Turing |title=The <math>p</math>-function in <math>\lambda</math>-<math>K</math>-conversion |journal=[[Journal of Symbolic Logic]] |volume=2 |number=4 |pages=164 |date=December 1937}}</ref><ref name="Barendregt.1985"/>{{rp|132}}
: <math>\Theta = (\lambda x y. y (x x y))\ (\lambda x y. y (x x y))\ .</math>


==> 120
Its advantage over <math>\textsf{Y}</math> is that <math>\Theta\ f</math> beta-reduces to <math>f\ (\Theta f)</math>,<ref group="note">
</source>
{{nowrap|<math>\Theta\ f</math>}}
{{nowrap|<math>\equiv</math>}}
{{nowrap|<math>(\lambda xy.y(xxy))\ (\lambda xy.y(xxy))\ f</math>}}
{{nowrap|<math>\to</math>}}
{{nowrap|<math>( \lambda y.y\ ((\lambda xy.y(xxy))\ (\lambda xy.y(xxy))\ y) )\ f</math>}}
{{nowrap|<math>\to</math>}}
{{nowrap|<math>f\ ((\lambda xy.y(xxy))\ (\lambda xy.y(xxy))\ f)</math>}}
{{nowrap|<math>\equiv</math>}}
{{nowrap|<math>f\ (\Theta\ f)</math>}}
</ref>
whereas <math>\textsf{Y}\ f</math> and <math>f\ (\textsf{Y} f)</math> only beta-reduce to a common term.<ref group=note name="Y demonstration"/>


===Other fixed-point combinators===
<math>\Theta</math> also has a simple call-by-value form:
In untyped lambda calculus fixed-point combinators are not especially rare. In fact there are infinitely many of them.<ref name="bimbo"/> In 2005 Mayer Goldberg showed that the set of fixed-point combinators of untyped lambda calculus is [[recursively enumerable]].<ref name=gold>Goldberg, 2005</ref>
: <math>\Theta_{v} = (\lambda x y. y (\lambda z. x x y z))\ (\lambda x y. y (\lambda z. x x y z))\ .</math>


The '''Y''' combinator can be expressed in the [[SKI combinator calculus|SKI-calculus]] as
The analog for [[mutual recursion]] is a ''polyvariadic fix-point combinator'',<ref>{{cite web |url=http://okmij.org/ftp/Computation/fixed-point-combinators.html#Poly-variadic |title=Many faces of the fixed-point combinator |website=okmij.org}}</ref><ref>[http://osdir.com/ml/lang.haskell.cafe/2003-10/msg00211.html Polyvariadic Y in pure Haskell98] {{webarchive|url=https://web.archive.org/web/20160304101809/http://osdir.com/ml/lang.haskell.cafe/2003-10/msg00211.html |date=2016-03-04}}, lang.haskell.cafe, October 28, 2003</ref><ref>{{cite web |url=https://stackoverflow.com/questions/4899113/fixed-point-combinator-for-mutually-recursive-functions |title=recursion - Fixed-point combinator for mutually recursive functions? |website=Stack Overflow}}</ref> which may be denoted Y*.


: '''Y''' = S (K (S I I)) (S (S (K S) K) (K (S I I)))
===Strict fixed-point combinator===


The simplest fixed point combinator in the SK-calculus, found by [[John Tromp]], is
In a [[strict programming language]] the Y combinator will expand until stack overflow, or never halt in case of tail call optimization.<ref>{{cite web |last1=Bene |first1=Adam |title=Fixed-Point Combinators in JavaScript |url=https://blog.benestudio.co/fixed-point-combinators-in-javascript-c214c15ff2f6 |website=Bene Studio |publisher=Medium |access-date=2 August 2020 |language=en |date=17 August 2017}}</ref> The ''Z'' combinator will work in strict languages (also called eager languages, where applicative evaluation order is applied). The ''Z'' combinator has the next argument defined explicitly, preventing the expansion of ''Z'' g in the right-hand side of the definition:<ref>{{cite web |title=CS 6110 S17 Lecture 5. Recursion and Fixed-Point Combinators |url=https://www.cs.cornell.edu/courses/cs6110/2017sp/lectures/lec05.pdf |website=Cornell University |at=4.1 A CBV Fixed-Point Combinator}}</ref>
: <math>Z g v = g (Z g) v\ .</math>


: '''Y'''' = S S K (S (K (S S (S (S S K)))) K)
and in lambda calculus it is an eta-expansion of the ''Y'' combinator:
: <math>Z = \lambda f.(\lambda x.f (\lambda v.x x v)) \ (\lambda x.f (\lambda v.x x v))\ .</math>


which corresponds to the lambda expression
=== Non-standard fixed-point combinators ===


: '''Y'''' = (λx. λy. x y x) (λy. λx. y (x y x))
In untyped lambda calculus there are terms that have the same [[Böhm tree]] as a fixed-point combinator, that is they have the same infinite extension λx.x (x (x ... )). These are called ''non-standard fixed-point combinators''. Any fixed-point combinator is also a non-standard one, but not all non-standard fixed-point combinators are fixed-point combinators because some of them fail to satisfy the equation that defines the "standard" ones. These strange combinators are called ''strictly non-standard fixed-point combinators''; an example is the following combinator:
: <math>N = B M (B (B M) B)</math>
where
: <math>B = \lambda x y z.x (y z)</math>
: <math>M = \lambda x.x x\ .</math>
The set of non-standard fixed-point combinators is not recursively enumerable.<ref name=gold/>


This fixed-point combinator is simpler than the Y combinator, and β-reduces into the Y combinator; it is sometimes cited as the Y combinator itself:
==Implementation in other languages==


: '''X''' = λf.(λx.x x) (λx.f (x x))
(The Y combinator is a particular implementation of a fixed-point combinator in lambda calculus. Its structure is determined by the limitations of lambda calculus. It is not necessary or helpful to use this structure in implementing the fixed-point combinator in other languages.)


Another common fixed point combinator is the Turing fixed-point combinator (named after its discoverer, [[Alan Turing]]):
Simple examples of fixed-point combinators implemented in some [[programming paradigm]]s are given below.


: '''Θ''' = (λx. λy. (y (x x y))) (λx. λy. (y (x x y)))
=== Lazy functional implementation ===


It also has a simple call-by-value form:
In a language that supports [[lazy evaluation]], like in [[Haskell (programming language)|Haskell]], it is possible to define a fixed-point combinator using the defining equation of the fixed-point combinator which is conventionally named <code>fix</code>. Since Haskell has lazy datatypes, this combinator can also be used to define fixed points of data constructors (and not only to implement recursive functions). The definition is given here, followed by some usage examples. In Hackage, the original sample is: <ref>[https://hackage.haskell.org/package/base-4.10.0.0/docs/src/Data.Function.html#fix The original definition in Data.Function].</ref>


: '''Θ'''<sub>'''v'''</sub> = (λx. λy. (y (λz. x x y z))) (λx. λy. (y (λz. x x y z)))
<syntaxhighlight lang="haskell">
fix, fix' :: (a -> a) -> a
fix f = let x = f x in x -- Lambda dropped. Sharing.
-- Original definition in Data.Function.
-- alternative:
fix' f = f (fix' f) -- Lambda lifted. Non-sharing.


Some fixed point combinators, such as this one (constructed by [[Jan Willem Klop]]) are useful chiefly for amusement:
fix (\x -> 9) -- this evaluates to 9


fix (\x -> 3:x) -- evaluates to the lazy infinite list [3,3,3,...]
: '''Y<sub>k</sub>''' = (L L L L L L L L L L L L L L L L L L L L L L L L L L)


where:
fact = fix fac -- evaluates to the factorial function
where fac f 0 = 1
fac f x = x * f (x-1)


fact 5 -- evaluates to 120
: L = λabcdefghijklmnopqstuvwxyzr. (r (t h i s i s a f i x e d p o i n t c o m b i n a t o r))
</syntaxhighlight>


=== Strictly non-standard fixed-point combinators ===
=== Strict functional implementation ===


In untyped lambda calculus there are terms that have the same [[Böhm tree]] as a fixed-point combinator, that is they have the same infinite extension λx.x(x(x ... )). These are called '''non-standard fixed-point combinators'''. Evidently, any fixed-point combinator is also a non-standard one, but not all non-standard fixed-point combinators are fixed-point combinators because some of them fail to satisfy the equation that defines the "standard" ones. These strange combinators are called '''strictly non-standard fixed-point combinators'''; an example is the following combinator N = BM(B(BM)B), where B = λxyz.x(yz) and M = λx.xx. The set of non-standard fixed-point combinators is not recursively enumerable.<ref name=gold/>
In a strict functional language, the argument to ''f'' is expanded beforehand, yielding an infinite call sequence,
: <math>f\ (f ... (f\ (\mathsf{fix}\ f))... )\ x</math>.


==Implementing fixed-point combinators==
This may be resolved by defining fix with an extra parameter.
In a language that supports [[lazy evaluation]], like in Haskell, it is possible to define a fixed-point combinator using the defining equation of the fixed-point combinator. A programming language of this kind effectively solves the fixed-point combinator equation by means of its own (lazy) recursion mechanism. This implementation of a fixed-point combinator in Haskell is sometimes referred to as defining the '''Y''' combinator in Haskell.<!--which copied the wrong stuff from this article over years most likely--> This is incorrect because the actual '''Y''' combinator is rejected by the Haskell [[type checker]]<ref>Haskell mailing list thread on [http://groups.google.co.uk/group/fa.haskell/browse_frm/thread/f0a62b6de1416d8b How to define Y combinator in Haskell], 15 Sep 2006</ref> (but see the following section for a small modification of '''Y''' using a recursive type which works). The listing below shows the implementation of a fixed-point combinator in Haskell that exploits Haskell's ability to solve the fixed-point combinator equation. This fixed-point combinator is traditionally called <code>fix</code> in Haskell. Observe that <code>fix</code> is a polymorphic fixed-point combinator (c.f. the discussion in previous section on [[System F]]); its type is only shown for clarity—it can be [[type inference|inferred]] in Haskell. The definition is followed by some usage examples.


<!--<source lang=Haskell> renders const as keyword, which is very misleading in this case -->
<syntaxhighlight lang=ocaml>
<pre>
let rec fix f x = f (fix f) x (* note the extra x; here fix f = \x-> f (fix f) x *)
fix :: (a -> a) -> a
fix f = f (fix f)


fix (const 9) -- const is a function that returns its first parameter and ignores the second;
let factabs fact = function (* factabs has extra level of lambda abstraction *)
-- this evaluates to 9
0 -> 1
| x -> x * fact (x-1)


let _ = (fix factabs) 5 (* evaluates to "120" *)
factabs fact 0 = 1 -- factabs is F from our lambda calculus example
factabs fact x = x * fact (x-1)
</syntaxhighlight>


(fix factabs) 5 -- evaluates to 120
===Imperative language implementation===
</pre>


In the example above, the application of <code>fix</code> does not loop infinitely, because of lazy evaluation; e.g., in the expansion of <code>fix (const 9)</code> as <code>(const 9)(fix f)</code>, the subexpression <code>fix f</code> is not evaluated. In contrast, the definition of fix from Haskell loops forever when applied in a [[strict programming language]], because the argument to f is expanded beforehand, yielding an infinite call sequence <code>f (f ... (fix f) ... ))</code>, which causes a [[stack overflow]] in most implementations.
This example is a slightly interpretive implementation of a fixed-point combinator. A class is used to contain the ''fix'' function, called ''fixer''. The function to be fixed is contained in a class that inherits from fixer. The ''fix'' function accesses the function to be fixed as a virtual function. As for the strict functional definition, ''fix'' is explicitly given an extra parameter ''x'', which means that lazy evaluation is not needed.


<!--TODO: rewrite from here -->
<syntaxhighlight lang="cpp">
In a strict language like [[OCaml]], we can avoid the infinite recursion problem by forcing the use of a [[Closure_(computer_science)|closure]]. The strict version of <code>fix</code> shall have the type ∀a.∀b.((a→b)→(a→b))→(a→b). In other words, it works only on a function which itself takes and returns a function. For example, the following OCaml code implements a strict version of <code>fix</code>:
template <typename R, typename D>
<source lang=ocaml>
class fixer
let rec fix f x = f (fix f) x (* note the extra x *)
{
public:
R fix(D x)
{
return f(x);
}
private:
virtual R f(D) = 0;
};


let factabs fact = function (* factabs now has extra level of lambda abstraction *)
class fact : public fixer<long, long>
0 -> 1
{
virtual long f(long x)
| x -> x * fact (x-1)
{
if (x == 0)
{
return 1;
}
return x * fix(x-1);
}
};


let _ = (fix factabs) 5 (* evaluates to "120" *)
long result = fact().fix(5);
</source>
</syntaxhighlight>


The same idea can be used to implement a ([[monomorphic]]) fixed combinator in strict languages that support [[inner class]]es inside methods (called local inner classes in [[Java (programming language)|Java]]), which are used as 'poor man's closures' in this case. Even when such classes may be anonymous, as in the case of Java, the syntax is still cumbersome. [http://arcfn.com/2009/03/y-combinator-in-arc-and-java.html Java code]. [[Function object]]s, e.g. in [[C++]], simplify the calling syntax, but they still have to be generated, preferably using a helper function such as [http://www.boost.org/doc/libs/1_36_0/libs/bind/bind.html boost::bind]. [http://stackoverflow.com/questions/152084/fixed-point-combinators-in-c/154267#154267 C++ code].
In an imperative-functional language, such as [[Lisp (programming language)|Lisp]], [[Scheme (programming language)|Scheme]], or [[Racket (programming language)|Racket]], Landin (1963){{full citation needed|date=December 2019}} suggests the use of a variable assignment to create a fixed-point combinator:


===Example of encoding via recursive types===
<syntaxhighlight lang="scheme">
In programming languages that support [[recursive type]]s (see [[System F-omega|System F<sub>ω</sub>]]), it is possible to type the Y combinator by appropriately accounting for the recursion at the type level. The need to self-apply the variable x can be managed using a type (Rec a), which is defined so as to be isomorphic to (Rec a -> a).
(define Y!
(lambda (f-maker)
((lambda (f)
(set! f (f-maker (lambda (x) (f x)))) ;; assignment statement
f)
'NONE)))
</syntaxhighlight>


For example, in the following Haskell code, we have In and out being the names of the two directions of the isomorphism, with types:
Using a lambda calculus with axioms for assignment statements, it can be shown that Y! satisfies the same fixed-point law as the call-by-value Y combinator:<ref>{{cite book |last=Felleisen |first=Matthias |title=The Lambda-v-CS Calculus |year=1987 |publisher=Indiana University |url=https://www2.ccs.neu.edu/racket/pubs/#felleisen87}}</ref><ref>{{cite book |last=Talcott |first=Carolyn |author-link=Carolyn Talcott |title=The Essence of Rum: A theory of the intensional and extensional aspects of Lisp-type computation |type=Ph.D. thesis |year=1985 |publisher=Stanford University}}</ref>
: <math>(\textsf{Y}_!\ \lambda x.e) e' = (\lambda x.e)\ (\textsf{Y}_!\ \lambda x.e) e'</math>


<source lang=haskell>
==Typing==

In [[System F]] (polymorphic lambda calculus) a polymorphic fixed-point combinator has type{{Citation needed|reason=Y may not be typable in system F with empty context.|date=August 2019}};
: ∀a.(a → a) → a
where ''a'' is a [[type variable]]. That is, ''fix'' takes a function, which maps a → a and uses it to return a value of type a.

In the simply typed lambda calculus extended with [[recursive data type]]s, fixed-point operators can be written, but the type of a "useful" fixed-point operator (one whose application always returns) may be restricted.

In the [[simply typed lambda calculus]], the fixed-point combinator Y cannot be assigned a type<ref>[http://cs.anu.edu.au/courses/COMP3610/lectures/Lambda.pdf An Introduction to the Lambda Calculus] {{webarchive |url=https://web.archive.org/web/20140408014716/http://cs.anu.edu.au/courses/COMP3610/lectures/Lambda.pdf |date=2014-04-08}}</ref> because at some point it would deal with the self-application sub-term <math>x~x</math> by the application rule:
: <math>{\Gamma\vdash x\!:\!t_1 \to t_2\quad\Gamma\vdash x\!:\!t_1}\over{\Gamma\vdash x~x\!:\!t_2}</math>

where <math>x</math> has the infinite type <math>t_1 = t_1\to t_2</math>. No fixed-point combinator can in fact be typed; in those systems, any support for recursion must be explicitly added to the language.

=== Type for the Y combinator ===

In programming languages that support [[recursive data type]]s, it is possible to type the Y combinator by appropriately accounting for the recursion at the type level. The need to self-apply the variable x can be managed using a type (<code>Rec a</code>), which is defined so as to be isomorphic to (<code>Rec a -> a</code>).

For example, in the following Haskell code, we have <code>In</code> and <code>out</code> being the names of the two directions of the isomorphism, with types:<ref>Haskell mailing list thread on [https://groups.google.com/g/fa.haskell/c/8KYrbeFBbYs How to define Y combinator in Haskell], 15 Sep 2006</ref><ref>{{cite book |last1=Geuvers |first1=Herman |url=http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.158.1478 |title=On Fixed point and Looping Combinators in Type Theory |last2=Verkoelen |first2=Joep |citeseerx=10.1.1.158.1478}}</ref>

<syntaxhighlight lang=haskell>
In :: (Rec a -> a) -> Rec a
In :: (Rec a -> a) -> Rec a
out :: Rec a -> (Rec a -> a)
out :: Rec a -> (Rec a -> a)
</source>
</syntaxhighlight>


which lets us write:
which lets us write:


<syntaxhighlight lang=haskell>
<source lang=haskell>
newtype Rec a = In { out :: Rec a -> a }
newtype Rec a = In { out :: Rec a -> a }


y :: (a -> a) -> a
y :: (a -> a) -> a
y = \f -> (\x -> f (out x x)) (In (\x -> f (out x x)))
y = \f -> (\x -> f (out x x)) (In (\x -> f (out x x)))
</source>
</syntaxhighlight>


Or equivalently in OCaml:
Or equivalently in OCaml:


<syntaxhighlight lang=ocaml>
<source lang=ocaml>
type 'a recc = In of ('a recc -> 'a)
type 'a recc = In of ('a recc -> 'a)
let out (In x) = x
let out (In x) = x


let y f = (fun x a -> f (out x x) a) (In (fun x a -> f (out x x) a))
let y f = (fun x a -> f (out x x) a) (In (fun x a -> f (out x x) a))
</source>
</syntaxhighlight>


=== Anonymous recursion by other means ===
Alternatively:
Although fixed point combinators are the standard solution for allowing a function [[name_binding|not bound]] to an identifier to call itself, some languages like [[JavaScript]] provide a syntactical construct which allows [[anonymous function]]s to refer to themselves. For example, in Javascript one can write the following,<ref name=ifworks/><ref>Mozilla Developer Center, [https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Functions_and_function_scope/arguments/callee#Examples arguments.callee Examples], Core JavaScript 1.5 Reference</ref> although it has been removed in the strict mode of the latest standard edition.


<syntaxhighlight lang=ocaml>
<source lang="javascript">
function(x) {
let y f = (fun x -> f (fun z -> out x x z)) (In (fun x -> f (fun z -> out x x z)))
return x === 0 ? 1 : x * arguments.callee(x-1);
</syntaxhighlight>
}

</source>
==General information==
Because fixed-point combinators can be used to implement recursion, it is possible to use them to describe specific types of recursive computations, such as those in [[fixed-point iteration]], [[iterative method]]s, [[recursive join]] in [[relational database]]s, [[data-flow analysis]], FIRST and FOLLOW sets of non-terminals in a [[context-free grammar]], [[transitive closure]], and other types of [[closure (mathematics)|closure]] operations.

A function for which ''every'' input is a fixed point is called an [[identity function]]. Formally:
: <math>\forall x (f\ x = x)</math>
In contrast to universal quantification over all <math>x</math>, a fixed-point combinator constructs ''one'' value that is a fixed point of <math>f</math>. The remarkable property of a fixed-point combinator is that it constructs a fixed point for an ''arbitrary given'' function <math>f</math>.

Other functions have the special property that, after being applied once, further applications don't have any effect. More formally:
: <math>\forall x (f\ (f\ x) = f\ x)</math>
Such functions are called [[idempotent]] (see also [[Projection (mathematics)]]). An example of such a function is the function that returns ''0'' for all even integers, and ''1'' for all odd integers.

In [[lambda calculus]], <!-- applying a generic fixed-point operator to a function known to be identity or an idempotent function is generally not interesting and yields trivial results.{{discuss}} F-->from a computational point of view, applying a fixed-point combinator to an identity function or an idempotent function typically results in non-terminating computation. For example, we obtain
: <math>(Y \ \lambda x.x) = (\lambda x.(xx) \ \lambda x.(xx))</math>
where the resulting term can only reduce to itself and represents an infinite loop.

Fixed-point combinators do not necessarily exist in more restrictive models of computation. For instance, they do not exist in [[simply typed lambda calculus]].

The Y combinator allows [[recursion (computer science)|recursion]] to be defined as a set of [[production (computer science)|rewrite rule]]s,<ref>{{cite book |title=The Little Lisper |author=[[Daniel P. Friedman]], [[Matthias Felleisen]] |publisher=[[Science Research Associates]] |year=1986 |chapter=Chapter 9 - Lambda The Ultimate |page=179}} "In the chapter we have derived a Y-combinator which allows us to write recursive functions of one argument without using define."</ref> without requiring native recursion support in the language.<ref>{{cite web |url=https://mvanier.livejournal.com/2897.html |title=The Y Combinator (Slight Return) or: How to Succeed at Recursion Without Really Recursing |author=Mike Vanier |url-status=live |archive-url=https://web.archive.org/web/20110822202348/http://mvanier.livejournal.com/2897.html |archive-date=2011-08-22}} "More generally, Y gives us a way to get recursion in a programming language that supports first-class functions but that doesn't have recursion built in to it."</ref>

In programming languages that support [[anonymous function]]s, fixed-point combinators allow the definition and use of anonymous [[recursion|recursive functions]], i.e. without having to [[name binding|bind]] such functions to [[identifier]]s. In this setting, the use of fixed-point combinators is sometimes called ''[[anonymous recursion]]''.<ref group=note>This terminology appears to be largely [[mathematical folklore|folklore]], but it does appear in the following:
* Trey Nash, ''Accelerated C# 2008'', Apress, 2007, {{ISBN|1-59059-873-3}}, p. 462—463. Derived substantially<!--gives him credit!--> from [http://www.linkedin.com/pub/wes-dyer/2/727/a39 Wes Dyer]'s blog (see next item).
* Wes Dyer [https://docs.microsoft.com/en-us/archive/blogs/wesdyer/anonymous-recursion-in-c Anonymous Recursion in C#], February 02, 2007, contains a substantially similar example found in the book above, but accompanied by more discussion.</ref><ref name=ifworks>The If Works [https://blog.jcoglan.com/2008/01/10/deriving-the-y-combinator/ Deriving the Y combinator], January 10th, 2008</ref>


==See also==
==See also==
* [[Fixed-point iteration]]
* [[Anonymous function]]
* [[Anonymous function]]
* [[Fixed-point iteration]]
* [[Eigenfunction]]
* [[Lambda calculus#Recursion and fixed points]]
* [[Lambda calculus#Recursion and fixed points|Lambda calculus]]
* [[Lambda lifting]]
* [[Let expression]]


==Notes==
==Notes==
{{reflist}}
{{Reflist|group=note}}

==References==
* Werner Kluge, ''Abstract computing machines: a lambda calculus perspective'', Springer, 2005, ISBN 3-540-21146-2, pp. 73–77<!-- covers the how it works section, but does not use Wikipedia/Javascript/C# terminology like "anonymous recursion". -->
* Mayer Goldberg, (2005) ''[http://www.brics.dk/RS/05/1/BRICS-RS-05-1.pdf On the Recursive Enumerability of Fixed-Point Combinators]'', BRICS Report RS-05-1, University of Aarhus<!--covers most of the theory-->
* [[Matthias Felleisen]]. [http://www.ps.uni-sb.de/courses/sem-prog97/material/YYWorks.ps A Lecture on the ''Why'' of ''Y''].


==References==
==References==
{{Reflist}}
{{Reflist}}
* Werner Kluge, ''Abstract computing machines: a lambda calculus perspective'', Springer, 2005, {{ISBN|3-540-21146-2}}, pp.&nbsp;73–77<!-- covers the how it works section, but does not use Wikipedia/Javascript/C# terminology like "anonymous recursion". -->
* Mayer Goldberg, (2005) ''[https://www.brics.dk/RS/05/1/BRICS-RS-05-1.pdf On the Recursive Enumerability of Fixed-Point Combinators]'', BRICS Report RS-05-1, University of Aarhus<!--covers most of the theory-->
* [[Matthias Felleisen]], [https://docs.google.com/viewer?url=https%3A%2F%2Fwww.ps.uni-saarland.de%2Fcourses%2Fsem-prog97%2Fmaterial%2FYYWorks.ps&embedded=true&chrome=false&dov=1 A Lecture on the ''Why'' of ''Y''].<!--https://www.ps.uni-saarland.de/courses/sem-prog97/material/YYWorks.ps is a PostScript file, unreadable by most users.-->


==External links==
==External links==
{{Wikibooks|Haskell/Fix and recursion}}
{{wikibooks|Haskell/Fix and recursion}}
* http://www.latrobe.edu.au/philosophy/phimvt/joy/j05cmp.html
* [https://hypercubed.github.io/joy/html/j05cmp.html Recursion Theory and Joy], Manfred von Thun, (2002 or earlier)
* http://okmij.org/ftp/Computation/fixed-point-combinators.html
* [http://cs.brown.edu/courses/cs173/2002/Lectures/2002-10-28-lc.pdf The Lambda Calculus - notes by Don Blaheta, October 12, 2000]
* [http://matt.might.net/articles/implementation-of-recursive-fixed-point-y-combinator-in-javascript-for-memoization/ "Fixed-point combinators in Javascript"]
* [http://www.ece.uc.edu/~franco/C511/html/Scheme/ycomb.html Y Combinator]
* http://www.cs.brown.edu/courses/cs173/2002/Lectures/2002-10-28-lc.pdf
* [https://www.sbf5.com/~cduan/technical/ruby/ycombinator.shtml "A Use of the Y Combinator in Ruby"]
* http://www.mactech.com/articles/mactech/Vol.07/07.05/LambdaCalculus/
* [https://okasaki.blogspot.com/2008/07/functional-programming-inada.html "Functional programming in Ada"]
* http://www.csse.monash.edu.au/~lloyd/tildeFP/Lambda/Examples/Y/ (executable)
* [https://rosettacode.org/wiki/Y_combinator Rosetta code - Y combinator]
* http://www.ece.uc.edu/~franco/C511/html/Scheme/ycomb.html
* [http://use.perl.org/~Aristotle/journal/30896 an example and discussion of a perl implementation]
* [http://www.ps.uni-saarland.de/courses/sem-prog97/material/YYWorks.ps "A Lecture on the Why of Y"]
* [http://www.eecs.harvard.edu/~cduan/technical/ruby/ycombinator.shtml "A Use of the Y Combinator in Ruby"]
* [http://okasaki.blogspot.com/2008/07/functional-programming-inada.html "Functional programming in Ada"]
* [http://bc.tech.coop/blog/070611.html "Y Combinator in Erlang"]
* [http://kestas.kuliukas.com/YCombinatorExplained/ "The Y Combinator explained with JavaScript"]
* [http://mvanier.livejournal.com/2897.html "The Y Combinator (Slight Return)"] (detailed derivation)
* [http://yinwang0.wordpress.com/2012/04/09/reinvent-y "How to Reinvent The Y Combinator "] (animated slides)




{{DEFAULTSORT:Fixed Point Combinator}}
{{DEFAULTSORT:Fixed Point Combinator}}
[[Category:Combinatory logic]]
[[Category:Fixed points (mathematics)]]
[[Category:Lambda calculus]]
[[Category:Lambda calculus]]
[[Category:Mathematics of computing]]
[[Category:Mathematics of computing]]
[[Category:Fixed points (mathematics)]]
[[Category:Combinatory logic]]
[[Category:Recursion]]
[[Category:Recursion]]

[[et:Püsipunktikombinaator]]
[[ja:不動点コンビネータ]]
[[pl:Operator paradoksalny]]
[[ru:Комбинатор неподвижной точки]]
[[zh:不动点组合子]]

Revision as of 16:24, 17 May 2022

In computer science, a fixed-point combinator (or fixpoint combinator[1]) is a higher-order function that computes a fixed point of other functions. A fixed point of a function f is a value that f doesn't change (x such that x = f(x) ).

Consider the function f(x) = x2. 0 and 1 are fixed points of this function, because 0 = 02 and 1 = 12. This function has no other fixed points.

A very different example comes up with many familiar markup languages. For example, HTML list elements contain item elements which contain paragraphs (p elements) which contain text, emphasis elements like b and i, and so on (this amounts to a Tree_(data_structure)). Given a set of elements, it is often useful also to retrieve all of their descendents. One way is to define a simpler function (call it f) that takes a set of elements, and returns a set that contains all of those elements plus all of their (direct) child elements. For example, if x is a set of 3 paragraphs, f(x) includes those 3 paragraphs and all of the emphasis elements, text, and other they directly contain. If you use f again (f(f(x))), you add all of x's grandchild elements. Repeating eventually accumulates all of the descendants. Then evaluating f again can never add any more elements. This is a fixed-point for f, because the set returned by f is the same as the set passed to f. Thus, a function g that applies f repeatedly and returns the set of original elements and all their descendants, is a fixed-point combinator.

The Recursive join in relational databases is very similar.

The function for which any input is a fixed point is called the Identity function. Other functions have the special property that after being applied once, further applications don't have any effect. More formally: f(f(x)) = f(x) for all x. Such functions are called idempotent. An example of such a function is the function that returns 0 for all even integers, and 1 for all odd integers.

When f is a first-order function (a function on "simple" values such as integers), a fixed point is a first-order value. However, the notion also applies to higher-order functions: f takes another function p such that p = f(p). A fixed-point combinator, then, is a function g which produces such a fixed point p for any function f:

g(f) = p, where p = f(p)

or, alternatively:

g(f) = f(g(f)).

Because fixed-point combinators are higher-order functions, their history is intimately related to the development of lambda calculus. One well-known fixed-point combinator in the untyped lambda calculus is Haskell Curry's Y = λf·(λx·f (x x)) (λx·f (x x)). The name of this combinator is sometimes incorrectly used to refer to any fixed-point combinator. The untyped lambda calculus however, contains an infinitude of fixed-point combinators.[2] Fixed-point combinators do not necessarily exist in more restrictive models of computation. For instance, they do not exist in simply typed lambda calculus.

In programming languages that support anonymous functions, fixed-point combinators allow the definition and use of anonymous recursive functions, i.e. without having to bind such functions to identifiers. In this setting, the use of fixed-point combinators is sometimes called anonymous recursion.[3][4]

How it works

Ignoring for now the question whether fixed-point combinators even exist (to be addressed in the next section), we illustrate how a function satisfying the fixed-point combinator equation works. To trace the computation steps, we choose untyped lambda calculus as our programming language. The (computational) equality from the equation that defines a fixed-point combinator corresponds to beta reduction in lambda calculus.

As a concrete example of a fixed-point combinator applied to a function, we use the standard recursive mathematical equation that defines the factorial function

fact(n) = if n = 0 then 1 else n * fact(n − 1)

We can express a single step of this recursion in lambda calculus as the lambda abstraction

F = λf. λn. IFTHENELSE (ISZERO n) 1 (MULT n (f (PRED n)))

using the usual encoding for booleans, and Church encoding for numerals. The functions in CAPITALS above can all be defined as lambda abstractions with their intuitive meaning; see lambda calculus for their precise definitions. Intuitively, f in F is a place-holder argument for the factorial function itself.

We now investigate what happens when a fixed-point combinator FIX is applied to F and the resulting abstraction, which we hope would be the factorial function, is in turn applied to a (Church-encoded) numeral.

(FIX F) n = (F (FIX F)) n                       --- expanded the defining equation of FIX
          = (λx. IFTHENELSE (ISZERO x) 1 (MULT x ((FIX F) (PRED x)))) n  --- expanded the first F
          = IFTHENELSE (ISZERO n) 1 (MULT n ((FIX F) (PRED n)))          --- applied abstraction to n.

If we now abbreviate FIX F as FACT, we recognize that any application of the abstraction FACT to a Church numeral calculates its factorial

FACT n = IFTHENELSE (ISZERO n) 1 (MULT n (FACT (PRED n))).

Recall that in lambda calculus all abstractions are anonymous, and that the names we give to some of them are only syntactic sugar. Therefore, it's meaningless in this context to contrast FACT as a recursive function with F as somehow being "not recursive". What fixed point operators really buy us here is the ability to solve recursive equations. That is, we can ask the question in reverse: does there exist a lambda abstraction that satisfies the equation of FACT? The answer is yes, and we have a "mechanical" procedure for producing such an abstraction: simply define F as above, and then use any fixed point combinator FIX to obtain FACT as FIX F.

In the practice of programming, substituting FACT at a call site by the expression FIX F is sometimes called anonymous recursion. In the lambda abstraction F, FACT is represented by the bound variable f, which corresponds to an argument in a programming language, therefore F need not be bound to an identifier. F however is a higher-order function because the argument f is itself called as a function, so the programming language must allow passing functions as arguments. It must also allow function literals because FIX F is an expression rather than an identifier. In practice, there are further limitations imposed on FIX, depending on the evaluation strategy employed by the programming environment and the type checker. These are described in the implementation section.

Existence of fixed-point combinators

In certain mathematical formalizations of computation, such as the untyped lambda calculus and combinatory logic, every expression can be considered a higher-order function. In these formalizations, the existence of a fixed-point combinator means that every function has at least one fixed point; a function may have more than one distinct fixed point.

In some other systems, for example in the simply typed lambda calculus, a well-typed fixed-point combinator cannot be written. In those systems any support for recursion must be explicitly added to the language. In still others, such as the simply-typed lambda calculus extended with recursive types, fixed-point operators can be written, but the type of a "useful" fixed-point operator (one whose application always returns) may be restricted. In polymorphic lambda calculus (System F) a polymorphic fixed-point combinator has type ∀a.(a→a)→a, where a is a type variable.

Y combinator

One well-known (and perhaps the simplest) fixed-point combinator in the untyped lambda calculus is called the Y combinator. It was discovered by Haskell B. Curry, and is defined as:

Y = λf.(λx.f (x x)) (λx.f (x x))

We can see that this function acts as a fixed-point combinator by applying it to an example function g and rewriting:

Y g = (λf . (λx . f (x x)) (λx . f (x x))) g (by definition of Y)
= (λx . g (x x)) (λx . g (x x)) (β-reduction of λf: applied to g)
= g ((λx . g (x x)) (λx . g (x x))) (β-reduction of λx: applied left function to right function)
= g (Y g) (by second equality)

In programming practice, the above Y combinator is useful only in those languages that provide a non-strict evaluation strategy, such as call-by-name, since (Y g) diverges (for any g) in call-by-value settings.

For call-by-value languages, we need to eta-expand the self-applications (x x) inside the combinator, resulting in the call-by-value Y combinator (also called the Z combinator):

Y = (λx.f (λv.((x x) v))) (λx.f (λv.((x x) v)))

The following examples demonstrate the use of the call-by-value Y combinator (or the Z combinator).


Example in Scheme

(define Y
  (lambda (f)
    ((lambda (x) (f (lambda (v) ((x x) v))))
     (lambda (x) (f (lambda (v) ((x x) v)))))))

Factorial definition using Y Combinator

(define fact
  (Y (lambda (f)
       (lambda (n)
         (if (= n 0)
             1
             (* n (f (- n 1))))))))

Example in Common Lisp

(defun Y (f)
  ((lambda (x)
     (funcall f (lambda (v)
                  (funcall (funcall x x) v))))
   (lambda (x)
     (funcall f (lambda (v)
                  (funcall (funcall x x) v))))))

Factorial program using the Y combinator

(funcall
 (Y (lambda (f)
      (lambda (n)
	(if (= n 1) 
            1 
            (* n (funcall f (- n 1))))))) 5)

=> 120

Example in Erlang

-module(y_comb).
-export([y2/1]).

y2(F) ->
  (fun (G) -> G(G) end)(
    fun (G) ->
      F(fun (X, Y) -> (G(G))(X, Y) end)
    end
  ).

List reversing using the Y combinator

(y_comb:y2(
  fun (R) ->
    fun
      ([H|T], L) -> R(T, [H|L]);
      ([],    L) -> L
    end
  end
))(" eoJ", "Armstrong").

=> "Joe Armstrong"

Example in JavaScript

Y combinator function[5]

function Y(f) {
    return 
    ((function (x) {
        return f(function (v) { return x(x)(v); }); })
     (function (x) {
         return f(function (v) { return x(x)(v); }); })
    );
}

Factorial function using the Y combinator

var factorial = Y(function (fac) {
    return function (n) {
        if (n == 0) { return 1; }
        else { return n * fac(n - 1); }
    };
});

factorial(5);

==> 120


Example in Python

def Y(f):
    return (lambda x: f(lambda v: x(x)(v))) (lambda x: f(lambda v: x(x)(v)))

def fact(f):
    return (lambda n: 1 if (n == 0) else n * f(n - 1))

Alternatively, with further use of lambda abstractions

Y = (lambda f: (lambda x: f(lambda v: x(x)(v)))(lambda x: f(lambda v: x(x)(v))))

fact = (lambda f: (lambda n: 1 if (n == 0) else n * f(n - 1)))

Y(fact)(5)

==> 120

Other fixed-point combinators

In untyped lambda calculus fixed-point combinators are not especially rare. In fact there are infinitely many of them.[2] In 2005 Mayer Goldberg showed that the set of fixed-point combinators of untyped lambda calculus is recursively enumerable.[6]

The Y combinator can be expressed in the SKI-calculus as

Y = S (K (S I I)) (S (S (K S) K) (K (S I I)))

The simplest fixed point combinator in the SK-calculus, found by John Tromp, is

Y' = S S K (S (K (S S (S (S S K)))) K)

which corresponds to the lambda expression

Y' = (λx. λy. x y x) (λy. λx. y (x y x))

This fixed-point combinator is simpler than the Y combinator, and β-reduces into the Y combinator; it is sometimes cited as the Y combinator itself:

X = λf.(λx.x x) (λx.f (x x))

Another common fixed point combinator is the Turing fixed-point combinator (named after its discoverer, Alan Turing):

Θ = (λx. λy. (y (x x y))) (λx. λy. (y (x x y)))

It also has a simple call-by-value form:

Θv = (λx. λy. (y (λz. x x y z))) (λx. λy. (y (λz. x x y z)))

Some fixed point combinators, such as this one (constructed by Jan Willem Klop) are useful chiefly for amusement:

Yk = (L L L L L L L L L L L L L L L L L L L L L L L L L L)

where:

L = λabcdefghijklmnopqstuvwxyzr. (r (t h i s i s a f i x e d p o i n t c o m b i n a t o r))

Strictly non-standard fixed-point combinators

In untyped lambda calculus there are terms that have the same Böhm tree as a fixed-point combinator, that is they have the same infinite extension λx.x(x(x ... )). These are called non-standard fixed-point combinators. Evidently, any fixed-point combinator is also a non-standard one, but not all non-standard fixed-point combinators are fixed-point combinators because some of them fail to satisfy the equation that defines the "standard" ones. These strange combinators are called strictly non-standard fixed-point combinators; an example is the following combinator N = BM(B(BM)B), where B = λxyz.x(yz) and M = λx.xx. The set of non-standard fixed-point combinators is not recursively enumerable.[6]

Implementing fixed-point combinators

In a language that supports lazy evaluation, like in Haskell, it is possible to define a fixed-point combinator using the defining equation of the fixed-point combinator. A programming language of this kind effectively solves the fixed-point combinator equation by means of its own (lazy) recursion mechanism. This implementation of a fixed-point combinator in Haskell is sometimes referred to as defining the Y combinator in Haskell. This is incorrect because the actual Y combinator is rejected by the Haskell type checker[7] (but see the following section for a small modification of Y using a recursive type which works). The listing below shows the implementation of a fixed-point combinator in Haskell that exploits Haskell's ability to solve the fixed-point combinator equation. This fixed-point combinator is traditionally called fix in Haskell. Observe that fix is a polymorphic fixed-point combinator (c.f. the discussion in previous section on System F); its type is only shown for clarity—it can be inferred in Haskell. The definition is followed by some usage examples.

fix :: (a -> a) -> a
fix f = f (fix f)

fix (const 9) -- const is a function that returns its first parameter and ignores the second;
              --  this evaluates to 9

factabs fact 0 = 1 -- factabs is F from our lambda calculus example
factabs fact x = x * fact (x-1)

(fix factabs) 5 -- evaluates to 120

In the example above, the application of fix does not loop infinitely, because of lazy evaluation; e.g., in the expansion of fix (const 9) as (const 9)(fix f), the subexpression fix f is not evaluated. In contrast, the definition of fix from Haskell loops forever when applied in a strict programming language, because the argument to f is expanded beforehand, yielding an infinite call sequence f (f ... (fix f) ... )), which causes a stack overflow in most implementations.

In a strict language like OCaml, we can avoid the infinite recursion problem by forcing the use of a closure. The strict version of fix shall have the type ∀a.∀b.((a→b)→(a→b))→(a→b). In other words, it works only on a function which itself takes and returns a function. For example, the following OCaml code implements a strict version of fix:

let rec fix f x = f (fix f) x (* note the extra x *)

let factabs fact = function (* factabs now has extra level of lambda abstraction *)
 0 -> 1
 | x -> x * fact (x-1)

let _ = (fix factabs) 5 (* evaluates to "120" *)

The same idea can be used to implement a (monomorphic) fixed combinator in strict languages that support inner classes inside methods (called local inner classes in Java), which are used as 'poor man's closures' in this case. Even when such classes may be anonymous, as in the case of Java, the syntax is still cumbersome. Java code. Function objects, e.g. in C++, simplify the calling syntax, but they still have to be generated, preferably using a helper function such as boost::bind. C++ code.

Example of encoding via recursive types

In programming languages that support recursive types (see System Fω), it is possible to type the Y combinator by appropriately accounting for the recursion at the type level. The need to self-apply the variable x can be managed using a type (Rec a), which is defined so as to be isomorphic to (Rec a -> a).

For example, in the following Haskell code, we have In and out being the names of the two directions of the isomorphism, with types:

In :: (Rec a -> a) -> Rec a
out :: Rec a -> (Rec a -> a)

which lets us write:

newtype Rec a = In { out :: Rec a -> a }

y :: (a -> a) -> a
y = \f -> (\x -> f (out x x)) (In (\x -> f (out x x)))

Or equivalently in OCaml:

type 'a recc = In of ('a recc -> 'a)
let out (In x) = x

let y f = (fun x a -> f (out x x) a) (In (fun x a -> f (out x x) a))

Anonymous recursion by other means

Although fixed point combinators are the standard solution for allowing a function not bound to an identifier to call itself, some languages like JavaScript provide a syntactical construct which allows anonymous functions to refer to themselves. For example, in Javascript one can write the following,[4][8] although it has been removed in the strict mode of the latest standard edition.

function(x) {
   return x === 0 ? 1 : x * arguments.callee(x-1);
}

See also

Notes

  1. ^ Peyton Jones, Simon L. (1987). The Implementation of Functional Programming. Prentice Hall International.
  2. ^ a b Bimbó, Katalin. Combinatory Logic: Pure, Applied and Typed. p. 48.
  3. ^ This terminology appear to be largely folklore, but it does appear in the following:
    • Trey Nash, Accelerated C# 2008, Apress, 2007, ISBN 1-59059-873-3, p. 462—463. Derived substantially from Wes Dyer's blog (see next item).
    • Wes Dyer Anonymous Recursion in C#, February 02, 2007, contains a substantially similar example found in the book above, but accompanied by more discussion.
  4. ^ a b The If Works Deriving the Y combinator, January 10th, 2008
  5. ^ Douglas Crockford, The Little JavaScripter: [1]
  6. ^ a b Goldberg, 2005
  7. ^ Haskell mailing list thread on How to define Y combinator in Haskell, 15 Sep 2006
  8. ^ Mozilla Developer Center, arguments.callee Examples, Core JavaScript 1.5 Reference

References

References

External links