* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Download Programming in the pure lambda
Survey
Document related concepts
Logic programming wikipedia , lookup
Monad (functional programming) wikipedia , lookup
Object-oriented programming wikipedia , lookup
C Sharp (programming language) wikipedia , lookup
APL syntax and symbols wikipedia , lookup
Falcon (programming language) wikipedia , lookup
Reactive programming wikipedia , lookup
Gene expression programming wikipedia , lookup
Recursion (computer science) wikipedia , lookup
Structured programming wikipedia , lookup
Standard ML 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 20037. 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 higherorder 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 (nonnegative) 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