Functions as data Massimo Merro 9 November 2011 Massimo Merro The Lambda language 1 / 21
The core of sequential programming languages In the mid 1960s, Peter Landin observed that a complex programming language can be understood by focussing on a tiny core calculus capturing the language s essential mechanisms...... together with a collection of convenient derived constructs whose behaviour is understood by translating them into the core calculus. The core language used by Landin was the λ-calculus, a formal system invented by Alonzo Church in the 1930 s as a universal language of computable functions. In 1960, John McCarthy published the design of the programming language Lisp based on the λ-calculus. Since then, the λ-calculus has seen a widespread use in the specification of programming language features in language design and implementation in the study of type systems. Massimo Merro The Lambda language 2 / 21
Expressiveness of the λ-calculus The λ-calculus can be viewed as a very simple programming language in which computations can be described as mathematical objects. It is a formal language in which every term denotes a function any term (function) can be applied to any other term, so functions are inherently higher-order Despite its simplicitiy, it is a Turing-complete language: it can express computations on natural number as does any other known programming language. Church s Thesis: any conceivable notion of computable function on natural numbers is equivalent to the λ-calculus. The force of Church s Thesis is that it postulates that all future notions of computation will be equivalent in expressive power to the λ-calculus. Massimo Merro The Lambda language 3 / 21
Encoding language features in λ-calculus The λ-calculus can be enriched in a variety of ways. It is often convenient to add special constructs for features like numbers, booleans, tuples, records, etc. However, all these features can be encoded in the λ-calculus, so they represent only syntactic sugar. Such extensions lead eventually to programming languages such as Lisp (McCarthy, 1960), ML (Milner et al., 1990), Haskell (Hudak et al., 1992), or Scheme (Sussman and Steele, 1975). In the next slide, we propose the Lambda language a simple extension of the λ-calculus with built-in operators for manipulating natural numbers and Booleans. Massimo Merro The Lambda language 4 / 21
The language Lambda M Lambda ::= n true false x (M 1 + M 1 ) (M 1 M 2 ) x Vars... M 1 and M 2 M 1 or M 2 M 1 = M 2 if M 1 then M 2 else M 3 λx.m M 1 M 2 The constructs in red constitute Churchs λ-calculus M 1 M 2 : apply function M 1 to argument M 2 λx.m: called λ-abstraction, define anonymous function which when applied to data N executes M{ N / x }. Massimo Merro The Lambda language 5 / 21
Examples of anonymous functions λx.x + 3 - from numerals to numerals λf.(f (2)) - from functions to numerals λx.if x = 0 then λy.y else λy.x - from numerals to functions λf.λx.if x = 0 then 1 else (let y = f (x 1)x y in ) - from functions to functions λf.λg.λx.f (g(x)) - from pairs of functions to functions. Massimo Merro The Lambda language 6 / 21
Conventions We recall the grammar given before describes the abstract syntax of the language; when we come to write terms down concretely we will often use brackets, as above, to clarify the structure of terms. So, to keep brackets to a minimum in this part, we will use some conventions for writing λ-terms: applications associates left: MNP means the same as (MN)P the scope of λ extends as far to the right as possible: λx.mn means λx.(mn), not (λx.m)n we sometimes write λxy.m for λx.λy.m. Massimo Merro The Lambda language 7 / 21
Free variables The construct λx.m for function definition is the only binding operator. In λx.m the variable x is bound in M which represents the scope of x. Let us provide a formal definition of free variables by structural induction: fv(x) = {x} fv(n) = fv(true) = fv(false) = fv(m 1 op M 2 ) = fv(m 1 ) fv(m 2 ) fv(m 1 M 2 ) = fv(m 1 ) fv(m 2 ) fv(λx.m) = fv(m) \ {x} Closed terms M is closed if fv(m) = Closed terms corresponds to programs. Massimo Merro The Lambda language 8 / 21
Small-step semantics Judgements: M N where M and N are programs Intuition: M performs one function application or one built-in operation and N remains to be evaluated. Massimo Merro The Lambda language 9 / 21
Semantic rules Standard rules: (S-Left) M 1 M 1 M 1 + M 2 M 1 + M 2 (S-Right) M 2 M 2 n 1 + M 2 n 1 + M 2 (S-Add) Similar rules for, and, if then else,... n 1 + n 2 n 3 n 3 = add(n 1, n 2 ) Massimo Merro The Lambda language 10 / 21
Function application To evaluate M 1 M 2 : First evaluate M 1 to a function λx.m Then it depends on the evaluation strategy. If Call-by-value: evaluate M 2 to a value v v Val ::= n true false λx.n evaluate M{ v / x }. If Call-by-name: evaluate M{ M 2 / x } Massimo Merro The Lambda language 11 / 21
Function application rules (L-App) M 1 M 1 M 1 M 2 M 1 M 2 Call-by-value: M 2 M (L-CBV.A) 2 (λx.m)m 2 (λx.m)m 2 where v Val ::= n true false (L-CBV) λx.n (λx.m)v M{ v / x } Call-by-name (L-CBN) (λx.m)m 2 M{ M 2 / x } where M 2 is a closed term (i.e. a program). Massimo Merro The Lambda language 12 / 21
Substitution of closed terms N closed: x{ N / x } = N y{ N / x } = y if y x (λx.m){ N / x } = λx.m (λy.m){ N / x } = λy.m{ N / x } if y x (M 1 M 2 ){ N / x } = (M 1 { N / x })(M 2 { N / x }) (M 1 op M 2 ){ N / x } = (M 1 { N / x }) op (M 2 { N / x })...... N open: requires α-conversion, i.e. renaming of bound variables. Massimo Merro The Lambda language 13 / 21
Self-application Is it possible to express non-terminating programs in Lambda? Yes, of course! For example, the divergent combinator Ω def = (λx.xx)(λx.xx) contains just one redex, and reducing this redex yields exactly Ω again! Ω Ω Ω...... Ω...... Non-termination is built-in in Lambda! Massimo Merro The Lambda language 14 / 21
Call-by-name vs. call-by-value Note that unlike the Fpl language, having function definitions among the constructs may lead to different results. They give different results: (λx.0)(ω) cbn 0 (λx.0)(ω) cbv (λx.0)(ω) cbv... cbv... Even more surprisingly: (λx.λy.x)(id 0) cbn λy.(id 0) (λx.λy.x)(id 0) cbv λy.0 For different evaluation strategies see Benjamin C. Pierce s Types and Programming Languages at pp. 56. Massimo Merro The Lambda language 15 / 21
Fixpoints In Mathematics, a fixpoint p (also known as an invariant point) of a function f is a point that is mapped to itself, i.e. f (p) = p. Fixpoints represent the core of what is known as recursion theory. Kleene s recursion theorems are a pair of fundamental results about the application of computable functions to their own descriptions. The two recursion theorems can be applied to construct fixed points of certain operations on computable functions, to generate quines 1, and to construct functions defined via recursive definitions. Kleene s recursion theorems is used to prove a fundamental result in computability theory: the Rice s Theorem! Rice s Theorem: For any non-trivial property of partial functions, there is no general and effective method to decide whether an algorithm computes a partial function with that property. 1 A quine is a computer program which produces a copy of its own source code as its only output. Massimo Merro The Lambda language 16 / 21
Fixpoints (via Turing s combinator) So, it is very important to prove that Lambda can express fixpoints. Let us define the two following terms in Lambda: A fix def = λx.λy.y(xxy) def = AA fix is a recursive function that given a term M returns a fixpoint of M, denoted with fix M. In fact, for any term M, using a call-by-name evaluation, we have: fix M (λy.y(aay))m M(fix M). Very often this operator is denoted using the greek letter Θ. Massimo Merro The Lambda language 17 / 21
Example: the factorial function F Fac def = λf.λx.if x = 0 then 1 else x f (x 1) def = ΘF ΘF λx.if x = 0 then 1 else x ΘF (x 1) ΘF 3 (λx.if x = 0 then 1 else x ΘF (x 1)) 3 3 ΘF 2 3 (λx.if x = 0 then 1 else x ΘF (x 1)) 2 3 2 ΘF 1 3 2 1 ΘF 0 3 2 1 1 6 Massimo Merro The Lambda language 18 / 21
Fixpoints (via Curry s combinator) Notice that there are infinitely many forms of fixpoint combinators. Probably, the best known is the one by Curry and most commonly called Y : Y def = λf.(λx.f (xx))(λx.f (xx)) We can easily check that is has the required property: Y M def = ( λf. ( λx.f (xx))(λx.f (xx) )) M ( ) λx.m(xx))(λx.m(xx) M ( (λx.m(xx))(λx.m(xx)) ) = M(Y M) M ( (λx.m(xx))(λx.m(xx)) ) Massimo Merro The Lambda language 19 / 21
Applicative order fixpoint combinator The Curry Y -combinator does not work in languages like Lisp, because of the applicative order (call-by-value) reduction strategy: Y M will keep evaluating forever. A different fixpoint combinator does work, though: the applicative order fixpoint combinator which is λf.(λg.gg)(λx.f (λa.xxa)) or alternatively λf.(λx.f (λa.xxa))(λx.f (λa.xxa)) Exercise Check that this is a fixpoint combinator. Notice that it only differs from Curry s Y by replacing terms of the form xx with λa.xxa. Inserting this λ stops Lisp s evaluation mechanism from getting into an infinite loop. Massimo Merro The Lambda language 20 / 21
Some fun: Klop s fixpoint combinator Jan Klop came up with this ridiculous one: if we define where then L def = λabcdefghijklmnopqstuvwxyzr.r(thisisafixedpointcombinator) λabcdef... means λa.λb.λc.λd.λe.λf.... thisisafixe... means ((((((((((th)i)s)i)s)a)f )i)x)e)... LLLLLLLLLLLLLLLLLLLLLLLLLL is indeed a fixpoint combinator. (26 times) Exercise Check that Klop s combinator works. Hint: the phrase this is a fixed point combinator contains 27 letters. Massimo Merro The Lambda language 21 / 21