Movatterモバイル変換


[0]ホーム

URL:


Jump to content
WikipediaThe Free Encyclopedia
Search

Fixed-point combinator

From Wikipedia, the free encyclopedia
Higher-order function Y for which Y f = f (Y f)

Incombinatory logic forcomputer science, afixed-point combinator (orfixpoint combinator)[1]: p.26  is ahigher-order function (i.e., afunction that takes a function asargument) that returns somefixed point (a value that is mapped to itself) of its argument function, if one exists.

Formally, iffix{\displaystyle \mathrm {fix} } is a fixed-point combinator and the functionf{\displaystyle f} has one or more fixed points, thenfix f{\displaystyle \mathrm {fix} \ f} is one of these fixed points, i.e.,

fix f =f (fix f).{\displaystyle \mathrm {fix} \ f\ =f\ (\mathrm {fix} \ f).}

Fixed-point combinators can be defined in thelambda calculus and infunctional programming languages, and provide a means to allow forrecursive definitions.

Y combinator in lambda calculus

[edit]

In the classical untypedlambda calculus, every function has a fixed point. A particular implementation offix{\displaystyle \mathrm {fix} } isHaskell Curry's paradoxical combinator Y, given by[2]: 131 [note 1][note 2]

Y=λf. (λx.f (x x)) (λx.f (x x)){\displaystyle \mathrm {Y} =\lambda f.\ (\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))}

(Here using the standard notations and conventions of lambda calculus: Y is a function that takes one argumentf and returns the entire expression following the first period; the expressionλx.f (x x){\displaystyle \lambda x.f\ (x\ x)} denotes a function that takes one argumentx, thought of as a function, and returns the expressionf (x x){\displaystyle f\ (x\ x)}, where(x x){\displaystyle (x\ x)} denotesx applied to itself. Juxtaposition of expressions denotesfunction application, is left-associative, and has higher precedence than the period.)

Verification

[edit]

The following calculation verifies thatYg{\displaystyle \mathrm {Y} g} is indeed a fixed point of the functiong{\displaystyle g}:

Y g {\displaystyle \mathrm {Y} \ g\ }(λf.(λx.f (x x)) (λx.f (x x))) g   {\displaystyle \triangleq (\lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x)))\ g\ \ \ }by the definition ofY{\displaystyle \mathrm {Y} }
=β(λx.g (x x)) (λx.g (x x)) {\displaystyle =_{_{\beta }}(\lambda x.g\ (x\ x))\ (\lambda x.g\ (x\ x))\ }byβ-reduction: replacing the formal argumentf of Y with the actual argumentg
=βg ((λx.g (x x)) (λx.g (x x))) {\displaystyle =_{_{\beta }}g\ ((\lambda x.g\ (x\ x))\ (\lambda x.g\ (x\ x)))\ }by β-reduction: replacing the formal argumentx of the first function with the actual argument(λx.g (x x)){\displaystyle (\lambda x.g\ (x\ x))}
g (Y g){\displaystyle \equiv g\ (\mathrm {Y} \ g)}by second equality, above

The lambda termg (Y g){\displaystyle g\ (\mathrm {Y} \ g)} may not, in general,β-reduce to the termY g{\displaystyle \mathrm {Y} \ g}. However, both terms β-reduce to the same term, as shown.

Uses

[edit]

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 added variables may be used as a counter, or index. The resulting function behaves like awhile or afor loop in an imperative language.

Used in this way, the Y combinator implements simplerecursion. The lambda calculus does not allow a function to appear as a term in its own definition as is possible in manyprogramming languages, but a function can be passed as an argument to a higher-order function that applies it in a recursive manner.

The Y combinator may also be used in implementingCurry's paradox. The heart of Curry's paradox is that untyped lambda calculus is unsound as a deductive system, and the Y combinator demonstrates this by allowing an anonymous expression to represent zero, or even many values. This is inconsistent inmathematical logic.

Example implementations

[edit]

An example implementation of Y in the languageR is presented below:

Y<-\(f){g<-\(x)f(x(x))g(g)}

This can then be used to implement factorial as follows:

fact<-\(f)\(n)if(n==0)1elsen*f(n-1)Y(fact)(5)# yields 5! = 120

Y is only needed when function names are absent. Substituting all the definitions into one line so that function names are not required gives:

(\(f)(\(x)f(x(x)))(\(x)f(x(x))))(\(f)\(n)if(n==0)1elsen*f(n-1))(5)

This works because R useslazy evaluation.

Languages that usestrict evaluation, such asPython,C++, and otherstrict programming languages, can often express Y; however, any implementation is useless in practice since itloops indefinitely until terminating via astack overflow.

Fixed-point combinator

[edit]

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:

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.

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.

In the untyped lambda calculus, the function to apply the fixed-point combinator to may be expressed using an encoding, likeChurch 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.

Alternately, a function may be considered as a lambda term defined purely in lambda calculus.

These different approaches affect how a mathematician and a programmer may regard a fixed-point combinator. A mathematician may see the Y combinator applied to a function as being an expression satisfying the fixed-point equation, and therefore a solution.

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.

Values and domains

[edit]

Many functions do not have any fixed points, for instancef:NN{\displaystyle f:\mathbb {N} \to \mathbb {N} } withf(n)=n+1{\displaystyle f(n)=n+1}. UsingChurch encoding, natural numbers can be represented in lambda calculus, and this functionf can be defined in lambda calculus. However, itsdomain will now containall lambda expressions, not just those representing natural numbers. The Y combinator, applied tof, will yield a fixed-point forf, but this fixed-point won't represent anatural number. If trying to compute Yf in an actual programming language, an infinite loop will occur.

Function versus implementation

[edit]

The fixed-point combinator may be defined in mathematics and then implemented in other languages. General mathematics defines a function based on itsextensional properties.[3] That is, two functions are equal if they perform the same mapping. Lambda calculus and programming languages regard function identity as anintensional 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.

Definition of the term "combinator"

[edit]

Combinatory logic is ahigher-order functions theory. Acombinator is aclosed lambda expression, meaning that it has nofree variables. The combinators may be combined to direct values to their correct places in the expression without ever naming them as variables.

Recursive definitions and fixed-point combinators

[edit]

Fixed-point combinators can be used to implementrecursive definition of functions. However, they are rarely used in practical programming.[4]Strongly normalizingtype systems such as thesimply 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.[1]: page 232 

The factorial function

[edit]

Thefactorial function provides a good example of how a fixed-point combinator may be used to define recursive functions. The standard recursive definition of the factorial function in mathematics can be written as

fact n={1if n=0n×fact(n1)otherwise.{\displaystyle \operatorname {fact} \ n={\begin{cases}1&{\text{if}}~n=0\\n\times \operatorname {fact} (n-1)&{\text{otherwise.}}\end{cases}}}

wheren is a non-negativeinteger.Implementing this in lambda calculus, where integers are represented usingChurch encoding, encounters the problem that the lambda calculus disallows the name of a function ('fact') to be used in the function's definition. This can be circumvented using a fixed-point combinatorfix{\displaystyle {\textsf {fix}}} as follows.

Define a functionF of two argumentsf andn:

F f n=(IsZero n) 1 (multiply n (f (pred n))){\displaystyle F\ f\ n=(\operatorname {IsZero} \ n)\ 1\ (\operatorname {multiply} \ n\ (f\ (\operatorname {pred} \ n)))}

(Here(IsZero n){\displaystyle (\operatorname {IsZero} \ n)} is a function that takes two arguments and returns its first argument ifn=0, and its second argument otherwise;pred n{\displaystyle \operatorname {pred} \ n} evaluates ton–1.)

Now definefact=fix F{\displaystyle \operatorname {fact} ={\textsf {fix}}\ F}. Thenfact{\displaystyle \operatorname {fact} } is a fixed-point ofF, which gives

factn=F fact n=(IsZero n) 1 (multiply n (fact (pred n))) {\displaystyle {\begin{aligned}\operatorname {fact} n&=F\ \operatorname {fact} \ n\\&=(\operatorname {IsZero} \ n)\ 1\ (\operatorname {multiply} \ n\ (\operatorname {fact} \ (\operatorname {pred} \ n)))\ \end{aligned}}}

as desired.

Fixed-point combinators in lambda calculus

[edit]

The Y combinator, discovered byHaskell Curry, is defined as

Y=λf.(λx.f (x x)) (λx.f (x x)){\displaystyle Y=\lambda f.(\lambda x.f\ (x\ x))\ (\lambda x.f\ (x\ x))}

Other fixed-point combinators

[edit]

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

The Y combinator can be expressed in theSKI-calculus as

Y=S(K(SII))(S(S(KS)K)(K(SII)))=SS(S(S(KS)K))(K(SII)){\displaystyle {\mathsf {Y=S(K(SII))(S(S(KS)K)(K(SII)))=SS(S(S(KS)K))(K(SII))}}}

Additional combinators (B, C, K, W system) allow for much shorter encodings. WithU=SII{\displaystyle {\mathsf {U=SII}}} the self-application combinator, sinceS(Kx)yz=x(yz)=Bxyz{\displaystyle {\mathsf {S}}({\mathsf {K}}x)yz=x(yz)={\mathsf {B}}xyz} andSx(Ky)z=xzy=Cxyz{\displaystyle {\mathsf {S}}x({\mathsf {K}}y)z=xzy={\mathsf {C}}xyz}, the above becomes

Y=S(KU)(SB(KU))=BU(CBU)   ;  Y=SSI(BWB){\displaystyle {\mathsf {Y=S(KU)(SB(KU))=BU(CBU)}}\ \ \ ;\ \ {\mathsf {Y=SSI(BWB)}}}

The shortest fixed-point combinator in the SK-calculus using S and K combinators only, found byJohn Tromp, is

Y=SSK(S(K(SS(S(SSK))))K)=WC(SB(C(WC))){\displaystyle {\mathsf {Y'=SSK(S(K(SS(S(SSK))))K)=WC(SB(C(WC)))}}}

although note that it is not in normal form, which is longer. This combinator corresponds to the lambda expression

Y=(λxy.xyx)(λyx.y(xyx)){\displaystyle {\mathsf {Y}}'=(\lambda xy.xyx)(\lambda yx.y(xyx))}

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:

X=λf.(λx.xx)(λx.f(xx))   ;  Xf=U(BfU){\displaystyle {\mathsf {X}}=\lambda f.(\lambda x.xx)(\lambda x.f(xx))\ \ \ ;\ \ {\mathsf {Xf=U(BfU)}}}

Another common fixed-point combinator is the Turing fixed-point combinator (named after its discoverer,Alan Turing):[7][2]: 132 

Θ=(λxy.y(xxy)) (λxy.y(xxy))=SII(S(K(SI))(SII))=U(B(SI)U){\displaystyle \Theta =(\lambda xy.y(xxy))\ (\lambda xy.y(xxy))={\mathsf {SII(S(K(SI))(SII))=U(B(SI)U)}}}

Its advantage overY{\displaystyle {\mathsf {Y}}} is thatΘ f{\displaystyle \Theta \ f} beta-reduces tof (Θf){\displaystyle f\ (\Theta f)},[note 3]whereasY f{\displaystyle {\mathsf {Y}}\ f} andf (Yf){\displaystyle f\ ({\mathsf {Y}}f)} only beta-reduce to a common term.

Θ{\displaystyle \Theta } also has a simplecall-by-value form:

Θv=(λxy.y(λz.xxyz)) (λxy.y(λz.xxyz)){\displaystyle \Theta _{v}=(\lambda xy.y(\lambda z.xxyz))\ (\lambda xy.y(\lambda z.xxyz))}

The analog formutual recursion is apolyvariadic fix-point combinator,[8][9][10] which may be denoted Y*.

Strict fixed-point combinator

[edit]

In astrict programming language the Y combinator will expand untilstack overflow, or never halt in case oftail call optimization.[11] The Z combinator will work instrict languages (also called eager languages, where applicative evaluation order is applied). The Z combinator has the next argument defined explicitly, preventing the expansion ofZg{\displaystyle Zg} in the right-hand side of the definition:[12]

Zgv=g(Zg)v .{\displaystyle Zgv=g(Zg)v\ .}

and in lambda calculus it is aneta-expansion of the Y combinator:

Z=λf.(λx.f(λv.xxv)) (λx.f(λv.xxv)) .{\displaystyle Z=\lambda f.(\lambda x.f(\lambda v.xxv))\ (\lambda x.f(\lambda v.xxv))\ .}

Non-standard fixed-point combinators

[edit]

If F is a fixed-point combinator in untyped lambda calculus, then there is:

F=λx.Fx=λx.x(Fx)=λx.x(x(Fx))={\displaystyle {\mathsf {F}}=\lambda x.Fx=\lambda x.x(Fx)=\lambda x.x(x(Fx))=\cdots }

Terms that have the sameBöhm tree as a fixed-point combinator, i.e., have the same infinite extensionλx.x(x(x)){\displaystyle \lambda x.x(x(x\cdots ))}, are callednon-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 fixed-point equation that defines the "standard" ones. These combinators are calledstrictly non-standard fixed-point combinators; an example is the following combinator:

N=BU(B(BU)B){\displaystyle {\mathsf {N=BU(B(BU)B)}}}

where

B=λxyz.x(yz){\displaystyle {\mathsf {B}}=\lambda xyz.x(yz)}
U=λx.xx {\displaystyle {\mathsf {U}}=\lambda x.xx\ }

since

N=λx.Nx=λx.x(N2x)=λx.x(x(x(N3x)))=λx.x(x(x(x(x(x(N4x))))))={\displaystyle {\mathsf {N}}=\lambda x.Nx=\lambda x.x(N_{2}x)=\lambda x.x(x(x(N_{3}x)))=\lambda x.x(x(x(x(x(x(N_{4}x))))))=\cdots }

whereNi{\displaystyle {\mathsf {N}}_{i}} are modifications ofN{\displaystyle {\mathsf {N}}} created on the fly which addi{\displaystyle i} instances ofx{\displaystyle x} at once into the chain while being replaced withNi+1{\displaystyle {\mathsf {N}}_{i+1}}.

The set of non-standard fixed-point combinators is notrecursively enumerable.[6]

Implementation in other languages

[edit]

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.

Simple examples of fixed-point combinators implemented in someprogramming paradigms are given below.

Lazy functional implementation

[edit]

In a language that supportslazy evaluation, as inHaskell, it is possible to define a fixed-point combinator using the defining equation of the fixed-point combinator which is conventionally namedfix. Since Haskell has lazydata types, 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:[13]

fix,fix'::(a->a)->afixf=letx=fxinx-- Lambda dropped. Sharing.-- Original definition in Data.Function.-- alternative:fix'f=f(fix'f)-- Lambda lifted. Non-sharing.fix(\x->9)-- this evaluates to 9fix(\x->3:x)-- evaluates to the lazy infinite list [3,3,3,...]fact=fixfac-- evaluates to the factorial functionwherefacf0=1facfx=x*f(x-1)fact5-- evaluates to 120

Strict functional implementation

[edit]

In a strict functional language, as illustrated below withOCaml, the argument tof is expanded beforehand, yielding an infinite call sequence,

f (f...(f (fix f))...) x{\displaystyle f\ (f...(f\ ({\mathsf {fix}}\ f))...)\ x}.

This may be resolved by defining fix with an extra parameter.

letrecfixfx=f(fixf)x(* note the extra x; hence fix f = \x-> f (fix f) x *)letfactabsfact=function(* factabs has extra level of lambda abstraction *)0->1|x->x*fact(x-1)let_=(fixfactabs)5(* evaluates to "120" *)

In a multi-paradigm functional language (one decorated with imperative features), such asLisp,Peter Landin suggested the use of a variable assignment to create a fixed-point combinator,[14] as in the below example usingScheme:

(defineY!(lambda(f)((lambda(g)(set!g(f(lambda(x)(gx))));; (set! g expr) assigns g the value of expr,g);; replacing g's initial value of #f, creating#f)));; thus the truly self-referential value in g

Using a lambda calculus with axioms for assignment statements, it can be shown thatY! satisfies the same fixed-point law as the call-by-value Y combinator:[15][16]

(Y! λx.e)e=(λx.e) (Y! λx.e)e{\displaystyle (Y_{!}\ \lambda x.e)e'=(\lambda x.e)\ (Y_{!}\ \lambda x.e)e'}

In more idiomatic modern Scheme usage, this would typically be handled via aletrec expression, aslexical scope was introduced to Lisp in the 1970s:

(defineY*(lambda(f)(letrec;; (letrec ((g expr)) ...) locally defines g((g;; as expr recursively: g in expr refers to(f(lambda(x)(gx)))));; that same g being defined, g = f (λx. g x)g)));; ((Y* f) ...) = (g ...) = ((f (λx. g x)) ...)

Or without the internal label:

(defineY*(lambda(f)((lambda(i)(ii))(lambda(i)(f(lambdax(apply(ii)x)))))))

Imperative language implementation

[edit]

This example is a slightly interpretive implementation of a fixed-point combinator. A class is used to contain thefix() function, calledFixedPointCombinator. The function to be fixed is contained in a class that inherits from fixer. Thefix() function accesses the function to be fixed using aconcept to callapply(). As for the strict functional definition,fix() is explicitly given an extra parameterx, which means that lazy evaluation is not needed.

usingstd::same_as;template<typenameRet,typenameArg,typenameT>conceptFixedPointApplicable=requires(Argx){{T::apply(x)}->same_as<Ret>;};template<typenameRet,typenameArg,FixedPointApplicable<Ret,Arg>Derived>classFixedPointCombinator{public:staticRetfix(Argx)noexcept{returnDerived::apply(x);}};classFactorial:publicFixedPointCombinator<long,long,Factorial>{staticlongapply(longx)noexcept{if(x==0){return1;}returnx*fix(x-1);}};longresult=Factorial::fix(5);

Using only lambdas, one can create a fixed-point combinator like so:

autofix=[](autof){return[f](auto&&...args)->decltype(auto){returnf(f,std::forward<decltype(args)>(args)...);};};autofactorial=fix([](autoself,longn)->long{returnn==0?1:n*self(self,n-1);});std::println("5! = {}",factorial(5));// prints 120

Another example can be shown to demonstrateSKI combinator calculus (with given bird name fromcombinatory logic) being used to build up Z combinator to achievetail call-like behavior through trampolining:

// CombinatorsconstK=<A,B>(a:A)=>(_b:B)=>a;// KestrelconstS=<A,B,C>(a:(x:C)=>(y:B)=>A)=>(b:(x:C)=>B)=>(c:C)=>a(c)(b(c));// Starling// Derived combinatorsconstI=S(K)(K);// IdentityconstB=S(K(S))(K);// BluebirdconstC=S(B(B)(S))(K(K));// CardinalconstW=C(S)(I);// WarblerconstT=C(I);// ThrushconstV=B(C)(T);// VireoconstI1=C(C(I));// Identity Bird Once Removed; same as C(B(B)(I))(I)constC1=B(C);// Cardinal Once RemovedconstR1=C1(C1);// Robin Once RemovedconstV1=B(R1)(C1);// Vireo Once RemovedconstI2=R1(V);// Identity Bird Twice Removed// Z combinatorsconstZ=B(W(I1))(V1(B)(W(I2)));constZ2=S(K(S(S(K(S(S(K)(K))(S(K)(K))))(S(K(S(K(S))(K)))(S(K(S(S(K)(K))))(K))))(K(S(S(K))))))(S(S(K(S(S(K(S(K(S))(K)))(S))(K(K))))(S(K(S(S(K(S(K(S))(K)))(S))(K(K))))(S(K(S))(K))))(K(S(S(K(S(S(K)(K))(S(K)(K))))(S(K(S(K(S))(K)))(S(K(S(S(K)(K))))(K))))(K(S(K(S(S(K(S(S(K(S(K(S))(K)))(S))(K(K))))(S(K(S(S(K(S(K(S))(K)))(S))(K(K))))(S(K(S(S(K)(K))))(K))))))(K))))));// Alternative fully expanded form.constZ3=S(S(K(S(S)(K(S(S(K)(K))(S(K)(K))))))(K))(S(S(K(S))(K))(K(S(S(K(S))(S(K(S(K(S(K(S(K(S(S)(K(K))))(K)))(S)))(S(S(K)(K)))))(K)))(K(K(S(S(K)(K))(S(K)(K))))))));// Another shorter version.consttrampoline=<T>(fn:T|(()=>T)):T=>{letctx=fn;while(typeofctx==="function"){ctx=(ctxas()=>T)();}returnctx;};constcountFn=(self:(n:number)=>any)=>(n:number):any=>n===0?(console.log(n),n):()=>self(n-1);// Return thunk "() => self(n - 1)" instead.// Examplestrampoline(Z(countFn)(10));trampoline(Z2(countFn)(10));trampoline(Z3(countFn)(10));

Typing

[edit]

InSystem F (polymorphic lambda calculus) a polymorphic fixed-point combinator has type[17]

∀a.(a → a) → a

wherea{\displaystyle a} is atype variable. That is, if the type offix f{\displaystyle \mathrm {fix} \ f} fulfilling the equationfix f = f (fix f){\displaystyle \mathrm {fix} \ f\ =\ f\ (\mathrm {fix} \ f)} isa{\displaystyle a}—the most general type—then the type off{\displaystyle f} isaa{\displaystyle a\to a}. So then,fix{\displaystyle \mathrm {fix} } takes a function that mapsa{\displaystyle a} toa{\displaystyle a} and uses it to return a value of typea{\displaystyle a}.

In the simply typed lambda calculus extended withrecursive data 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 thesimply typed lambda calculus, the fixed-point combinator Y cannot be assigned a type[18] because at some point it would deal with the self-application sub-termx x{\displaystyle x~x} by the application rule:

Γx:t1t2Γx:t1Γx x:t2{\displaystyle {\Gamma \vdash x\!:\!t_{1}\to t_{2}\quad \Gamma \vdash x\!:\!t_{1}} \over {\Gamma \vdash x~x\!:\!t_{2}}}

wherex{\displaystyle x} has the infinite typet1=t1t2{\displaystyle t_{1}=t_{1}\to t_{2}}. 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

[edit]

In programming languages that support namedrecursive data types, the unbounded recursion int:=ta{\displaystyle t:=t\to a}, which creates the would-be infinite typet{\displaystyle t}, is broken by naming the typet{\displaystyle t} explicitly, as e.g. typeR a{\displaystyle R\ a} which is defined so as to be isomorphic to (or just to be a synonym of) the typeR aa{\displaystyle R\ a\to a}. ThusR a:=R aa{\displaystyle R\ a:=R\ a\to a} is recognized as a recursive type. A datum (value) of the typeR a{\displaystyle R\ a} is created by simply tagging a value that is a function that has the typeR aa{\displaystyle R\ a\to a}, by the data constructor tag for the typeR a{\displaystyle R\ a}.

For example, in the following Haskell code, letRec be that tag name, and thusRec andapp be the names of the two directions of the isomorphism, with types:[19][20]

Rec::(Ra->a)->Raapp::Ra->(Ra->a)

(where::{\displaystyle ::} means "has type"). This lets us write:

newtypeRa=Rec{app::Ra->a}-- app (Rec g) = g    -- g :: R a -> a--      Rec g      :: R a-- app             :: R a -> (R a -> a)y::(a->a)->ayf=(\x->f(appxx))(Rec(\x->f(appxx)))--       x :: R a--   app x :: R a -> a-- app x x ::        a

The usual lambda-calculus definitionYf=U(λx.f (Ux)){\displaystyle \operatorname {Y} f=\operatorname {U} (\lambda x.f\ (\operatorname {U} x))} (whereUx=x x{\displaystyle \operatorname {U} x=x\ x}, and thus the termU(λx.f (Ux)){\displaystyle \operatorname {U} (\lambda x.f\ (\operatorname {U} x))} witnesses the equivalencex x=f (x x){\displaystyle x\ x=f\ (x\ x)}) contains at its core the self-applicationx x{\displaystyle x\ x}, which is untypeable in the statically typed programming languages. But the expressionapp x x aboveis typeable. Equivalently, we can redefine the self-application combinatorU{\displaystyle \operatorname {U} } and use it as

U'g=g(Recg)yf=U'(\Recg->f(U'g))--     g (Rec g)  =  f (g (Rec g))U''x=appxxyf=U''(Rec(\x->f(U''x)))--        app x x  =  f (app x x)

Or in OCaml:

type'arecc=Inof('arecc->'a)letout(Inx)=xletyf=(funxa->f(outxx)a)(In(funxa->f(outxx)a))

Alternatively:

letyf=(funx->f(funz->outxxz))(In(funx->f(funz->outxxz)))

Some languages allow for explicitly marking general types as being recursive even without naming them. Doing that also allows for defining theY{\displaystyle \operatorname {Y} } combinator in such languages.

General information

[edit]

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 infixed-point iteration,iterative methods,recursive join inrelational databases,data-flow analysis, FIRST and FOLLOW sets of non-terminals in acontext-free grammar,transitive closure, and other types ofclosure operations.

A function for whichevery input is a fixed point is called anidentity function. Formally:

x(f x=x){\displaystyle \forall x(f\ x=x)}

In contrast touniversal quantification over allx{\displaystyle x}, a fixed-point combinator constructsone value that is a fixed point off{\displaystyle f}. The remarkable property of a fixed-point combinator is that it constructs a fixed point for anarbitrary given functionf{\displaystyle f}.

Other functions have the special property that, after being applied once, further applications don't have any effect. More formally:

x(f (f x)=f x){\displaystyle \forall x(f\ (f\ x)=f\ x)}

Such functions are calledidempotent (see alsoProjection (mathematics)). An example of such a function is the function that returns 0 for all even integers, and 1 for all odd integers.

Inlambda calculus, 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, obtaining

(Y λx.x)=(λx.(λx.x)(x x)) (λx.(λx.x)(x x)){\displaystyle (Y\ \lambda x.x)=(\lambda x.(\lambda x.x)(x\ x))\ (\lambda x.(\lambda x.x)(x\ x))}

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 insimply typed lambda calculus.

The Y combinator allowsrecursion to be defined as a set ofrewrite rules,[21] without requiring native recursion support in the language.[22]

In programming languages that supportanonymous functions, fixed-point combinators allow the definition and use of anonymousrecursive functions, i.e., without having tobind such functions toidentifiers. In this setting, the use of fixed-point combinators is sometimes calledanonymous recursion.[note 4][23]

See also

[edit]

Notes

[edit]
  1. ^Throughout this article, the syntax rules given inLambda calculus § Notation are used, to save parentheses.
  2. ^According to Barendregt p.132, the name originated from Curry.
  3. ^Θ f{\displaystyle \Theta \ f}{\displaystyle \equiv }(λxy.y(xxy)) (λxy.y(xxy)) f{\displaystyle (\lambda xy.y(xxy))\ (\lambda xy.y(xxy))\ f}{\displaystyle \to }(λy.y ((λxy.y(xxy)) (λxy.y(xxy)) y)) f{\displaystyle (\lambda y.y\ ((\lambda xy.y(xxy))\ (\lambda xy.y(xxy))\ y))\ f}{\displaystyle \to }f ((λxy.y(xxy)) (λxy.y(xxy)) f){\displaystyle f\ ((\lambda xy.y(xxy))\ (\lambda xy.y(xxy))\ f)}{\displaystyle \equiv }f (Θ f){\displaystyle f\ (\Theta \ f)}
  4. ^This terminology appears to be largely [[mathematical folklore|]], but it appears in:
    • Trey Nash,Accelerated C# 2008, Apress, 2007,ISBN 1-59059-873-3, p. 462—463. Derived substantially fromWes Dyer's blog (see next item).
    • Wes DyerAnonymous Recursion in C#, February 02, 2007, contains a substantially similar example found in the book above, but accompanied by more discussion.

References

[edit]
  1. ^abPeyton Jones, Simon L. (1987).The Implementation of Functional Programming(PDF). Prentice Hall International.
  2. ^abHenk Barendregt (1985).The Lambda Calculus – Its Syntax and Semantics. Studies in Logic and the Foundations of Mathematics. Vol. 103. Amsterdam: North-Holland.ISBN 0444867481.
  3. ^Selinger, Peter (2001–2013)."Lecture notes on the lambda calculus: Expository course notes"(PDF). p. 6.
  4. ^"For those of us who don't know what a Y-Combinator is or why it's useful, ..."Hacker News. Retrieved2 August 2020.
  5. ^Bimbó, Katalin (27 July 2011).Combinatory Logic: Pure, Applied and Typed. CRC Press. p. 48.ISBN 9781439800010.
  6. ^abGoldberg, 2005
  7. ^Alan Mathison Turing (December 1937). "Thep{\displaystyle p}-function inλ{\displaystyle \lambda }-K{\displaystyle K}-conversion".Journal of Symbolic Logic.2 (4): 164.JSTOR 2268281.
  8. ^"Many faces of the fixed-point combinator".okmij.org.
  9. ^Polyvariadic Y in pure Haskell98Archived 2016-03-04 at theWayback Machine, lang.haskell.cafe, October 28, 2003
  10. ^"recursion - Fixed-point combinator for mutually recursive functions?".Stack Overflow.
  11. ^Bene, Adam (17 August 2017)."Fixed-Point Combinators in JavaScript".Bene Studio. Retrieved2 August 2020.
  12. ^"CS 6110 S17 Lecture 5. Recursion and Fixed-Point Combinators"(PDF).Cornell University. 4.1 A CBV Fixed-Point Combinator.
  13. ^The original definition in Data.Function.
  14. ^Landin, P. J. (January 1964). "The mechanical evaluation of expressions".The Computer Journal.6 (4):308–320.doi:10.1093/comjnl/6.4.308.
  15. ^Felleisen, Matthias (1987).The Lambda-v-CS Calculus. Indiana University.
  16. ^Talcott, Carolyn (1985).The Essence of Rum: A theory of the intensional and extensional aspects of Lisp-type computation (Ph.D. thesis). Stanford University.
  17. ^Girard, Jean-Yves (1986). "The systemF of variable types, fifteen years later".Theoretical Computer Science.45 (2):159–192.doi:10.1016/0304-3975(86)90044-7.MR 0867281. See especially p. 180.
  18. ^An Introduction to the Lambda CalculusArchived 2014-04-08 at theWayback Machine
  19. ^Haskell mailing list thread onHow to define Y combinator in Haskell, 15 September 2006
  20. ^Geuvers, Herman; Verkoelen, Joep,On Fixed point and Looping Combinators in Type Theory(PDF),CiteSeerX 10.1.1.158.1478
  21. ^Friedman, Daniel P.;Felleisen, Matthias (1986). "Chapter 9 - Lambda The Ultimate".The Little Lisper.Science Research Associates. p. 179.In the chapter we have derived a Y-combinator which allows us to write recursive functions of one argument without using define.
  22. ^Vanier, Mike (14 August 2008)."The Y Combinator (Slight Return) or: How to Succeed at Recursion Without Really Recursing".Archived from the original on 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.
  23. ^The If WorksDeriving the Y combinator, January 10, 2008

External links

[edit]
Wikibooks has a book on the topic of:Haskell/Fix and recursion
Retrieved from "https://en.wikipedia.org/w/index.php?title=Fixed-point_combinator&oldid=1337675918"
Categories:
Hidden categories:

[8]ページ先頭

©2009-2026 Movatter.jp