Lambda calculus
The lambda calculus represents a minimal core language just consisting of functions.
Functions are first-class (can appear as arguments and as return values). Think of first-class objects!
Lambda calculus: Syntax and semantics
Terms (expressions/programs) in the lambda calculus are defined by the following (abstract) EBNF syntax.
where
- refers to a variable
- to a lambda abstraction
- refers to a function application
The above EBNF is ambiguous. For example, consider expression . Based on the above EBNF rules there are two possible interpretations.
A lambda function with formal parameter where is applied to itself in the function body.
The identity function applied to .
The first interpretation can be explained via the following (parse tree) derivation
whereas for the second derivation we find
In the above derivation, parantheses "(..)" are meta-symbols to highlight the difference among the two derivations.
In examples, we therefore may make use of parantheses to avoid ambiguities, for example .
The operational semantics of the lambda calculus is defined via a single rule, commonly referred to as -reduction.
In , we apply the substitution : In the body , replace each occurence of the formal parameter by the argument .
Commonly, we refer to as a lambda-abstraction and to as the lambda-binding.
For example, reduces to .
As it is common in programming languages, we can reuse the same variable names and apply the common rule that each use of a variable name refers to the closest binding site. For example, consider where the innermost occurence of refers to the inner lambda-binding.
Via some simply renaming we can always guarantee that variables are distinct. For example, can be renamed to .
We generally assume that lambda-bindings always introduce fresh, distinct variables.
Details
Syntactic sugar
Function application is left associative. Hence, the term is a short-hand for .
The body of a lambda abstractions extends to the right as long as possible. Hence, the term is a short-hand for .
Free variables
computes all free variables in , i.e. those variables which are not bound by a lambda abstraction.
The definition by induction over the structure of is as follows:
In case of name clashes we must rename bound variables. Consider :
- Renaming yields:
- Substitution without name clashes yields:
Substitution
Examples (where refers to a term/expression)
- if !=
- if != and
Evaluation strategies
There are two principles ways how to evaluate terms:
- Innermost first
- Outermost first
Innermost
- Look for a redex innermost, leftmost
- Also called Applicative Order Reduction (AOR)
- Redex = reducible expression of the form
(\x-> ...) e
Call-by value (CBV) = AOR with the exception that we don't evaluate under "lambda" (i.e. inside function bodies)
Outermost
Outermost, leftmost, also called Normal Order Reduction (NOR)
Call-by name (CBN) = NOR with the exception that we don't evaluate under "lambda" (i.e. inside function bodies)
Examples
We consider evaluation of where denotes an evaluation step
Call-by-name (CBN)
Call-by-value (CBV):
(Non)Termination
There are lambda terms whose evaluation will not terminate:
Termination under CBN is more likely than termination under CBV.
Let's abbreviate by . Then, the lambda term terminates under CBN but not under CBV.
Short summary
- CBN seems rather inefficient
- But has plenty of applications
- More code reuse
- More declarative code
- Infinite, circular data structures
- ...
More details in some upcoming exercises.
Further examples
Consider .
Function application is left-associative, therefore, the meaning of the above lambda term is .
Let's carry out the evaluation steps.
because
because
On the other hand, the lambda term evaluates to . Briefly, the evaluation steps are
Expressiveness/Church Encoding
How expressive is the lambda calculus? As expressive as a Turing machine?
Yes! But how? The lambda calculus looks rather simple.
Indeed, but as we will show next, we can encode data types (booleans, ...), conditional expressions, recursion.
The idea of the Church encoding (named after Alonzo Church):
- Encode behavior not structure
Boolean operations
The idea:
not x = if x then false else true
x or y = if x then true else y
x and y = if x then y else false
The actual encoding:
true =
false =
if then else =
which written in 'lambda notation' is
ite =
Example: if true then else equals
In detail:
=
by adding parentheses (recall that function application is left associative)
Boolean operations example
We evaluate . The expect result is . Let's see!
- Replace short-hands by actual definitions.
- Rename variables such that all bound variables are distinct
- Introduce parantheses
- Carry out evaluation steps
Several redexes. We choose the outermost redex
Recursion
What about recursion?
factorial n = if n == 0 then 1
else n * (factorial (n-1))
Idea:
This works!
More encodings (pairs, natural numbers) are possible. See Church encoding.