Download Programming in the pure lambda

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Logic programming wikipedia , lookup

Monad (functional programming) wikipedia , lookup

Subroutine wikipedia , lookup

C syntax wikipedia , lookup

Object-oriented programming wikipedia , lookup

C Sharp (programming language) wikipedia , lookup

APL syntax and symbols wikipedia , lookup

?: wikipedia , lookup

Falcon (programming language) wikipedia , lookup

Reactive programming wikipedia , lookup

Gene expression programming wikipedia , lookup

C++ wikipedia , lookup

Recursion (computer science) wikipedia , lookup

Structured programming wikipedia , lookup

Standard ML wikipedia , lookup

Corecursion wikipedia , lookup

Functional programming wikipedia , lookup

Transcript
CITS3211
FUNCTIONAL PROGRAMMING
10. Programming in the pure λ­calculus
Summary: This lecture demonstates the power of the pure λ­
calculus by showing that there are λ­expressions that can be used to emulate some basic features of Haskell, namely definitions, booleans, pairs, lists, numbers and recursion.
 R.Davies 2003­7.
Pure λ­calculus
●
●
●
●
●
●
In the λ­calculus we can use built in constants to represent primitive Haskell values like True, False, 0, 1, 2, ..., ==, <, etc.
We can even represent if ... then ... else ... as a built in constant that is a primitive function taking three parameters:
if True E E' ↔ E
if False E E' ↔ E'
This is what we'll do in later lectures when we consider implementing interpreters and compilers for Haskell.
In this lecture we'll see that such constants are not essential. Instead the λ­calculus is powerful enough that we can implement Booleans, pairs, numbers, lists, etc. and even recursion.
When we restrict ourselves to λ­expressions without constants we say that we are using the pure λ­calculus.
Historically, the pure λ­calculus was studied first, by Alonzo Church in 1932 as a simple language that could express every computable function.
CITS3211 Functional Programming
2
10. Programming in the pure λ­calculus
λ­Expressions for Definitions
●
●
We want to be able to write code in the pure λ­calculus that is structured like a Haskell program.
Suppose our program has a bunch of definitions. E.g.
f x y = x
g z = z
●
Now, suppose we want to evaluate an expression. E.g.
f g g
●
We can turn the expression we want to evaluate into a function that takes expressions for f and g as arguments.
λfg. f g g
●
Then, the translation of the whole program is the application of this function to the translation of the definitions.
(λfg. f g g) (λxy. x) (λz. z)
●
The same technique can be used to translate local definitions (i. e. where definitions) into the λ­calculus.
CITS3211 Functional Programming
3
10. Programming in the pure λ­calculus
Booleans
●
We want to implement True, False and if as closed λ­
expressions in such a way that:
if True E E' ↔β
if False E E' ↔β
●
●
Everything is a function in the pure λ­calculus, so True and False must be some kind of functions.
We can make True and False the functions that take two arguments and select the first/second argument.
False = λxy.y
True = λxy.x
●
E
E'
Then if is a higher­order function that applies the first argument to the second and third.
if = λbxy. b x y
●
E.g. if True E E' = (λxyz. x y z) (λxy.x) E E' ↔β ( λyz. (λxy.x) y z) E E'
↔β E
●
Evaluation is done by β-reduction. CITS3211 Functional Programming
4
10. Programming in the pure λ­calculus
Pairs
●
●
●
To program with pairs, we need at least an operation mkPair to construct a pair from two expressions, and fst and snd to extract parts from a pair.
As before, in the λ­calculus pairs must be represented as some kind of functions.
We can consider a pair to be a function that takes a Boolean as input, and returns either the first or second part of the pair.
mkPair E E' True = E
mkPair E E' False = E'
●
●
●
Then we can represent mkPair by the λ­expression:
mkPair = λxyb. b x y
fst and snd each apply the pair to a selector function (i.e. True or False):
fst = λz.z (λxy.x)
snd = λz.z (λxy.y)
Then we can use β-reduction to show:
fst (mkPair E E') ↔β E
snd (mkPair E E') ↔β E'
CITS3211 Functional Programming
5
10. Programming in the pure λ­calculus
Lists
●
●
●
●
●
●
●
Lists are built using two constructor functions.
Nil for the empty list ([])
Cons h t for the list with head h and tail t (h:t)
We can represent lists using λ­expressions by combining the techniques from Booleans and pairs.
Nil is just the same selection function as True.
Nil = λxy.x
Cons h t is like the other “select”, but also combines features of similar to the pair (h, t).
Cons h t = λxy. y h t
Cons = λhtxy. y h t
Compare this to:
False = λxy.y
mkPair = λxyz. z x y
To check whether a list is empty, we can use the following function:
null = λl.l True (λht. False)
To take the head and tail of a list, or return a default d if the list is empty, we can use the following functions:
head = λld. l d (λht. h)
tail = λld. l d (λht. t)
CITS3211 Functional Programming
6
10. Programming in the pure λ­calculus
Numbers
●
●
●
●
●
●
●
●
As a first step in representing numbers using λ­
expressions, we show an inefficient representation of just the natural (non­negative) numbers.
Every natural number is either zero, or it is some other natural number plus one.
In Haskell we could represent natural numbers using the data type:
data Nat = Zero | Successor Nat
Zero is just the same selection function as True.
Zero = λxy.x
Successor a should be the other “select”, but also applies the second argument to a.
Successor a = λxy. y a
Successor = λaxy. y a
To check whether a number is zero, we can use the following function:
isZero = λn. n True (λm. False)
To find the predecessor of a number or return default d if the number is zero, we can use the following function:
predecessor = λnd. d (λm. m)
We can then write recursive functions for +, -, *, div, ==,
<, >, etc.
CITS3211 Functional Programming
7
10. Programming in the pure λ­calculus
Recursion
●
●
●
●
●
●
●
We can't write many interesting programs with lists or numbers unless we have recursion. E.g. we can't write the map function (or + or == for natural numbers).
But the λ−calculus doesn’t allow recursive definitions
Is there a way to emulate recursive definitions in the λ−
calculus?
If we have a recursive definition like
f = E
with E already converted to a λ−expression, then λf. E is a function that captures one “step” of the recursion, using the parameter f if further steps are required. Is there a λ−expression Y that repeatedly applies a λ−
expression f to itself?
Y f ↔β f (Y f) ↔β ... ↔β f (f (f (f ...) ) )
Yes. Let Y be the following λ−expression.
Y = λf . (λx. f (x x)) (λx. f (x x))
This is all we need to write recursive functions using pure λ−expressions.
CITS3211 Functional Programming
8
10. Programming in the pure λ­calculus
Why Y?
●
●
Why do we need Y?
Suppose we try to translate the following Haskell code for the map function into a pure λ­calculus expression.
map f [] = []
map f (h:t) = (f h) : (map f t)
●
We might try to translate this to the following expression for map.
λf l . l Nil (λh t. Cons (f h) (map f t))
●
●
But, this isn't a λ­calculus expression unless we know what term the map stands for!
We know what Nil and Cons stand for, so the above expression is an abbreviation for:
λf l . l (λxy.x) (λh t. (λabxy.y a b) (f h) (map f t))
●
But we don't know what to replace the map with. We'd like it to recursively stand for the whole expression! CITS3211 Functional Programming
9
10. Programming in the pure λ­calculus
Recursion using Y
●
●
●
●
This is where we need to use Y. To map we add an extra parameter m.
λm f l . l (λxy.x) (λh t. (λabxy.y a b) (f h) (m f t))
This is definitely a pure λ­expression, so let's give it a name, and go back to using the abbreviations Nil, Cons.
mapStep = λm f l . l Nil (λh t. Cons (f h) (m f t))
Now, mapStep doesn't do what we want unless we supply the argument m, which needs to be a function to do a map. Applying Y to mapStep does exactly this. Y mapStep ↔β mapStep (Y mapStep)
... ↔β mapStep (mapStep (mapStep ( ....)))
●
●
●
So, this pure λ­expression does what we want, so lets call it map.
map = Y mapStep
Note: Y is equivalent to the recursive Haskell function
y f = f (y f)
Also, Y is known as a fixed point combinator, because it finds an object that f maps to itself. Such fixed points play a key role in mathematical semantics of programs and an in the study of computability.
CITS3211 Functional Programming
10
10. Programming in the pure λ­calculus