Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Intuitionistic type theory wikipedia , lookup
C Sharp (programming language) wikipedia , lookup
Closure (computer programming) wikipedia , lookup
Standard ML wikipedia , lookup
Anonymous function wikipedia , lookup
Curry–Howard correspondence wikipedia , lookup
Lambda lifting wikipedia , lookup
Lambda calculus wikipedia , lookup
Computational lambda calculus: A combination of functional and imperative programming Anthi Anastasiou Bachelor of Science in Computer Science with Honours The University of Bath April 2009 Computational lambda calculus This dissertation may be made available for consultation within the University Library and may be photocopied or lent to other libraries for the purposes of consultation. Signed: I Computational lambda calculus Computational lambda calculus: A combination of functional and imperative programming. Submitted by: Anthi Anastasiou COPYRIGHT Attention is drawn to the fact that copyright of this dissertation rests with its author. The Intellectual Property Rights of the products produced as part of the project belong to the University of Bath (see http://www.bath.ac.uk/ordinances/#intelprop). This copy of the dissertation has been supplied on condition that anyone who consults it is understood to recognise that its copyright rests with its author and that no quotation from the dissertation and no information derived from it may be published without the prior written consent of the author. Declaration This dissertation is submitted to the University of Bath in accordance with the requirements of the degree of Bachelor of Science in the Department of Computer Science. No portion of the work in this dissertation has been submitted in support of an application for any other degree or qualification of this or any other university or institution of learning. Except where specifically acknowledged, it is the work of the author. Signed: II Computational lambda calculus Abstract Many programming languages have both functional and imperative features. They are combined in a number of different ways. One way, as in ML, can be studied systematically by modifying the simply typed lambda calculus to distinguish between values and computations. The modification is called the computational lambda calculus, and we study it here. III Computational lambda calculus Contents CONTENTS ………………………………………………………………………….. i LIST OF FIGURES …………………………………………………………………. iii LIST OF TABLES …………………………………………………………………... iv AKNOWLEDGEMENTS …………………………………………………………… v INTODUCTION ……………………………………………………………………... 1 LITERATURE SURVEY …………………………………………………………… 3 2.1 CATEGORY THEORY …………………………………………………… 4 2.1.1 Categories and functors ……………………………………….. 4 2.1.2 Initial and terminal objects …………………………………… 5 2.1.3 Products and coproducts………………………………………. 5 2.1.4 Adjoints …………………………….………………………… 6 2.1.5 Cartesian closed categories …………………………………... 7 2.2 UNTYPED LAMBDA CALCULUS ……………………………………… 7 2.2.1 Syntax of the untyped lambda calculus ……………………….. 8 Variable Binding …………….……………………………… 8 Higher-order functions ……………………………………... 8 Currying ………..…………………………………………… 9 2.2.2 Semantics of the untyped lambda calculus …………………… α-conversion and α-equivalence ……………………………. i 9 9 Computational lambda calculus Substitution ……………………….………………………… 10 β-reduction ………………………………………………….. 10 2.3 THE WORD PROBLEM FOR GROUPS …..…………………………….. 11 SIMPLY-TYPED LAMBDA CALCULUS ………………………………………… 12 3.1 SYNTAX OF SIMPLY-TYPED LAMBDA CALCULUS ………………….. 12 3.1.1Terms and types ………………………………………………... 12 3.2 SEMANTICS OF SIMPLY-TYPED LAMBDA CALCULUS …………….. 13 3.2.1 Typing rules …………………………………………………… 13 3.2.2 α-conversion and α-equivalence …………………………........ 14 3.2.3 β-reduction and β-equivalence ………………………………… 14 3.3 MODELS OF THE SIMPLY-TYPED LAMBDA CALCULUS …………... 15 COMPUTATIONAL LAMBDA CALCULUS ...…………………………………... 17 4.1 MONADS ……….………………………………………………………… 17 4.2 COMPUTATIONAL LAMBDA CALCULUS …………………………...... 21 4.2.1 Syntax of Computational Lambda Calculus …………………… 21 4.2.2 Semantics of Computational Lambda Calculus ………………. 22 4.3 A LEADING EXAMPLE: GLOBAL STATE …………...……………........ 23 IMPLEMENTATION AND TESTING ……………………………………………. 25 5.1. PROGRAM DOCUMENTATION AND TESTING ……………………… 25 CONCLUSIONS ……………………………………………………………... 32 6.1 ACHIEVEMENTS …………………………………………………. 32 6.2 FUTURE WORK …..………………………………………………. 32 6.3 PERSONAL REFLECTION ……………………………………….. 33 BIBLIOGRAPHY……………………………………………………………. 34 APPENDIX A………………………………………………………………… 36 ii Computational lambda calculus List of Figures FIGURE 1: Simply typed lambda calculus judgments …………………………… FIGURE 2: α-equality judgement ……………………………………………….... FIGURE 3: β-equality judgement ………………………………………………… FIGURE 4: η-equality judgement ………………………………………………… FIGURE 5: Seven equations of global state ............................................................. iii 14 14 15 15 23 Computational lambda calculus List of Tables TABLE 1: Representation of simply typed lambda terms ………………………... TABLE 2: Testing table 1 ………………………………………………………… TABLE 3: Testing table 2 ………………………………………………………… TABLE 4: Testing table 3 ……………………………………………………….... iv 25 26 30 31 Computational lambda calculus Acknowledgements I would like to thank my supervisor, John Power, for his support and encouragement throughout this project. Without his help and advice this project would be impossible. Many thanks also go to my family and friends for their love and support during my time at University. You are all amazing. Finally, special thanks to God who helped me in everything. I owe everything to you. v Computational lambda calculus Chapter 1 Introduction Functional programming languages, cf Lisp, have their roots in the lambda calculus and are widely known for their expressive power and simple semantics. Because of their mathematical simplicity are considered to be a great tool for formal analysis and program verification. Functional programs do not include assignment statements and so once a value is assigned to a variable, never changes. In general, functional programming languages do not include any side-effects at all. As a result, the number of bugs in a program is reduced and also the order of execution does not have any consequences on the result since no side-effects can alter the value of an expression. On the other hand, in imperative programming languages, cf FORTRAN, statements are executed in a sequential way and so the order of execution is really important. By allocating a value to a variable, any value that the variable might have held before is automatically destroyed. Imperative programming indicates programming with side effects. As a result, functions become harder to understand and also debugging gets more difficult. This is because by allowing an expression to produce side effects affects following computation. Despite that, some programming tasks, for instance input and output, can be viewed more easily in terms of operations with side-effects. Many programming languages offer the benefits of both functional and imperative programming and one way in which we can study this combination is by modifying the simply typed lambda calculus to distinguish between values and computations. This yields what is called the computational lambda calculus. Many papers have been written about computational lambda calculus but unfortunately people that are not familiar with the topic often find it difficult to understand. 1 Computational lambda calculus The main aim of this project is to gain an understanding of what computational lambda calculus and computational effects are and explain them in a simpler way in order to be understandable for a wider range of people. Then implement the simply typed lambda calculus and modify it into the computational lambda calculus. After that, try and consider computational lambda calculus together with an effect such as state and work out when two words are equal. Chapter 2 describes the basic concepts of category theory and the untyped lambda calculus needed to facilitate the understanding of the simply-typed lambda calculus which is described in Chapter 3. Chapter 4 presents the idea of monads and why they are useful in computer science. Then we continue by explaining computational lambda calculus and global state. Chapter 5 follows with the documentation of the program developed and the testing and we conclude this project with Chapter 6 where we discuss the achievements and possible extensions of the project. 2 Computational lambda calculus Chapter 2 Literature Survey The main aim of this project is to understand the concept of computational lambda calculus and computational effects. In order to be able to complete the project, there are some areas that need to be investigated. The basis for this project is category theory and lambda calculus. Category theory is a branch of pure mathematics that needs to be well understood since is the basis in every paper I will have to read later on. For the purpose of establishing notation we briefly recall the untyped lambda calculus. This will facilitate the understanding of simply typed and computational lambda calculus. It will be helpful to identify differences between them and why they are so useful. Another basic concept for this project is the logic of computational effects. Many papers have been written about what computational effects are and why they are useful. As this project is intended at producing a piece of software that will check when two words are equal, it will be useful, and indeed essential, to understand the concept and be able to recognize when two terms are equal. The next phase of the research process will be to identify and investigate previous work completed in the area like “The word problem for groups” and the major results. It will be useful to explore similar problems to identify and avoid possible mistakes that could be done and think of improvements that can be made. 3 Computational lambda calculus 2.1. Category Theory Category theory is a very important tool in theoretical computer science. It is a branch of pure mathematics that has found its major applications in the area of the semantics of programming languages. Thus far, it has influenced several parts of computer science, such us the design of functional and imperative programming languages, implementation techniques for functional languages, specification languages, as well as the development of algorithms. This section covers some fundamental concepts of category theory which will help me understand more difficult papers I will have to read later on, in order to complete this project. Definitions of this chapter were taken from the book “Basic category theory for computer scientists” written by Benjamin C. Pierce[5]. 2.1.1. Categories and Functors Definition 2.1. A category C comprises: • a collection of objects • a collection of arrows (often called morphisms) • operations assigning to each arrow f an object dom f, its domain, and an object cod f, its co domain (we write : → to show that = and = ; the collection of all arrows with domain A and co domain B is written , ); • a composition operator assigning to each pair of arrows f and g, with = , a composite arrow ∘ : → , satisfying the following associative law: o for any arrows : → , : → and ℎ: → (with A,B,C and D not necessarily distinct), ℎ ∘ ∘ = ℎ ∘ ∘ ; • for each object A, an identity arrow : → satisfying the following identity law: o for any arrow : → , ∘ = and ∘ = Example 2.1. The category Set is probably one of the most commonly used category in mathematics. Its objects are sets and its arrows are function. Example 2.2. The category of Monoids which has monoids as objects and monoid homomorphism as arrows. 4 Computational lambda calculus Definition 2.2. Let C and D be categories. A functor : → is a map taking each C-object A to a D-object F(A) and each C-arrow : → to a D-arrow : → , such that for all C-objects A and composable C-arrows f and g • = • ∘ = ∘ . A simple but important class of functors is the forgetful functors, usually denoted by the letter U. This class of functors is called forgetful because it consists of functors that “forget” structure or properties. Example 2.3. : → ! sends each group into its underlying set and group homomorphisms to set maps. Example 2.4. : " → sends each abelian group to its underlying group and acts as identity on arrows. This forgetful functor forgets the property of being abelian. Another very simple functor is the identity functor on any category C. The identity functor IC sends every object and arrow of the category to itself. 2.1.2. Initial and Terminal Objects Definition 2.3. An object 0 is called an initial object if, for every object A, there is exactly one arrow from 0 to A. Definition 2.4. Dually, an object 1 is called a terminal or final object if, for every object A, there is exactly one arrow from A to 1. Example 2.5. In the category ! of sets, the empty set is the initial object and every one-element set is a terminal object. Example 2.6. In the category #$% of monoids, the one-element monoid with only the identity element is the initial and terminal object too. In a situation where an object is both initial and terminal is called a zero object. 2.1.3. Products and Coproducts Definition 2.5. A product of two objects A and B is an object × together with two projection arrows '( : × → and ') : × → , such that for any object 5 Computational lambda calculus and pair of arrows : → and : → there is exactly one mediating arrow *, +: → × making the diagram 9 *, + × '( ') commute – that is, such that ,- ∘ *.. /+ = . and ,0 ∘ *.. /+ = /. If a category has a product × for each pair of objects and , we say that category has all binary products. Also, a category is said to have all finite products if and only if it has a terminal object and all binary products. Definition 2.6. A coproduct of two objects and is an object + , together with two injection arrows 2( : → + and 2) : → + , such that for any object and pair of arrows : → and : → there is exactly one arrow 3, 4: + → making the following diagram commute: 2) 2( + 3, 4 2.1.4. Adjoints A fundamental concept of category theory is the notion of adjoints. The last few years, the concept of adjoints is considered to be category theory’s main tool to distinguish what is important in mathematics. A lot of significant mathematical results are illustrated by the existence of adjoints. Definition 2.7. An adjunction consists of • a pair of categories C and D • a pair of functors : → and. : → • a natural transformation 5: 67 → ∘ ; 6 Computational lambda calculus such that for each C-object X and C-arrow : : → ;, there is a unique D-arrow # : : → ; such that the following triangle commutes: 5> : : # ; , is an adjoint pair of functors; F is the left adjoint of G and G is the right adjoint of F. The natural transformation 5 is called the unit of the adjunction. 2.1.5. Cartesian Closed Categories The notion of cartesian closed categories is one of the important links between category theory and computer science. The relationship between cartesian closed categories and the lambda calculus yields to the categorical abstract machine [18]; an implementation technique for functional programming languages. Also the simply typed lambda calculus can be modelled in any cartesian closed category (see definition 3.1 in section 3.3). Definition 2.8. A cartesian closed category is a category with finite products for which, for every object : of , the functor − × :: → has a right adjoint, which we shall write as : → −[14] With the definition of the cartesian closed categories we completed the basic ideas needed for the purposes of this project. 2.2. Untyped Lambda Calculus “Whatever the next 700 languages turn out to be, they will surely be variants of lambda calculus.” (Landin 1966) Lambda calculus[12][14] is a formal mathematical system developed by Alonzo Church in the 1930s. It provides the basis for many programming languages, especially functional programming languages like Lisp, Haskell and ML. Lambda calculus can be considered a functional language in its own right as it relies on the theory of mathematical functions. It was created as a type of computational logic in order to help with the study of functions, function application and recursion. It was proved that it is comparable to a universal Turing machine. 7 Computational lambda calculus Lambda calculus is based on a formal language ? = @, , where @ is a set of variables and a set of constants. The symbols , , A and the dot are also a part of the language. Language L is considered to be the smallest language over the alphabet = @ ∪ ∪ C, , A, . D. 2.2.1. Syntax of Untyped Lambda Calculus A lambda term is defined by the grammar ≔ F|AF. | ′ where: • F is a variable • AF. is an abstraction and • ′ is an application (application associates left, so ′ ′′ means ′ ′′ ) Variable Binding In an abstraction AF. , the operator A binds all free occurrences of its variable F in the body of the abstraction . is considered to be the scope of F, and we say that F is bounded in AF. . All other non bound variables in are called free. Example 2.1. AF. HHF In this lambda expression, F is a bound variable and H is free Sometimes an occurrence of a variable may refer to different things in different context like the next example. Example 2.2. AF. FAF. FF. A term in which all variables are bound is called a closed term. An open term is a lambda term in which not all variables are bound. Higher-Order Functions In lambda calculus, higher-order functions can be defined. These are functions that can take functions as input and/or return functions as outputs. 8 Computational lambda calculus Example 2.3. IA1K In this example our lambda term (which is a function), takes a function as an input and applies it to 1. Currying In the syntax described above, we only define functions that have exactly one argument. The lambda calculus does not provide any built-in support for multiargument functions [14]. In order to produce the same result in lambda calculus we can use higher-order functions that return functions as results. We can transform a function that accepts multiple arguments in a way that it can be invoked as a chain of functions each one, with a single argument. Example 2.4. , F = F is a function that has two arguments. In lambda calculus we can express it as a lambda abstraction AF. AH. ) which is LMNNO = A. AF. F which is a function of one argument that, when applied, returns as an output another function that accepts a second argument and then calculates a result in the same way as . This technique was invented by Moses Schönfinkel and Gottlob Frege, and it is known as Currying, in honour of the American mathematician and logician Haskell Curry. 2.2.2. Semantics of Untyped Lambda Calculus α-conversion and α-equivalence Two terms that differ only in the names of their bound variables are syntactically identical and considered to be α-equivalent. The process of changing the name of the bound variables is called α-conversion. While α-converting it is important to rename only variable occurrences that that are bound to the same abstraction to avoid variables getting captured by a different abstraction or get as a result a term with a different meaning from the original. Example 2.5. AF. F can be converted to AH. H Example 2.6. AF. AF. F can be converted to AH. AF. F but not AH. AF. H 9 Computational lambda calculus Substitution Substitution 3 ′ /F4 corresponds to the replacement of all free occurrences of F in by expression ′ and is defined as follows: F3 H3 AF. 3 AH. 3 ( ) 3 ′/F4 ′/F4 ′ /F4 ′ /F4 ′/F4 = ′ =H = AF. = AU. 3U/H43 ′ /F4 = ( 3 ′ /F4 ) 3 ′ /F4 if y ≠ x because all x in e are bound from λx. e where z is a not free variable in e or e′ As you can see here in order to avoid variables to be captured from any variables in ′, we first α-convert the lambda abstraction and then perform the substitution. β-reduction β-reduction is the basic reduction step in lambda calculus. A β-reducible expression or β-redex is an expression of the form AF. ′; a lambda application whose lefthand-side is an lambda abstraction. Example 2.7. AF. FFAF. HAH. H In the above example we have more than one redex. The whole term AF. FFAF. HAH. H is a redex but also AF. HAH. H is another redex in the same lambda term. β-reduction is the process of reducing a β-redex AF. ′ to 3 ′ /F4 and we write ′ if ′ results from β-reducing any sub term of . → β AH. FAH. F Example 2.8. AF. FFAH. F F → → β β AF. FAF. F AF. F Example 2.9. AF. FFAF. F → → β β AF. FFAF. FF Example 2.10. AF. FFAF. FF ….. → → β β The last term we get after β-reducing a lambda term is said to be in normal form if it contains no redexes. The last example is known as the Ω combinator and it reduces to itself. According to Church-Rosser theorem if two lambda terms have the same normal form then they are consider to be equal. 10 Computational lambda calculus 2.3 The Word Problem for Groups A similar problem to this project is the famous issue of “The word problem for groups”. In abstract algebra, the word problem for groups is the difficulty of deciding whether two specified words describe the same element of a group G. A major issue in the area has been to try to give an algorithm for when two "words" are equal to each other. A word is a product of generators, and two such words even though sometimes may appear to be different, they can indicate the same element of a group. This is possible because you can transform one word into the other when applying the group axioms (associativity, identity and invertibility) and relations. The word problem of groups is only concerned with finitely presented groups, which are groups with a finite number of generators and relations. The problem is to derive an algorithm which successfully terminates for any two given words and as its output it has the answer whether the two given words represent the same group element. In 1952, Pyotr Sergeyevich Novikov proved that there does not exist an effective algorithm for this problem, but there are some non-effective algorithms like KnuthBendix[7] algorithm and Todd-Coxeter algorithm and which do not necessarily terminate, but if they do, they have the answer as their output. The relationship of this problem with this project can be found in chapter 4 (section 4.3) after the description of global state. 11 Computational lambda calculus Chapter 3 Simply-Typed Lambda Calculus In the untyped lambda calculus, like Lisp, there is no limitation on how we can use objects. Every term is regarded as being of the same type and so we can apply anything as a function to anything else. In Lisp, even though there are distinctions between manipulated objects (lists, symbols, numbers or functions) there is no static type checking. Static typing is a way of assuring that operations are applied only to suitable objects and the programs delivered are more reliable. Lisp is dynamically typed but not many languages are like this. Most of the times information manipulated are provided with their types. In Java, for example we have as primitive types int, char, float, double and boolean. Other types can be defined using classes and new types can be defined by the users. Also the Java API provides build in types. Simply typed lambda calculus[15], cf ML, written λ→ , is a variant of the untyped lambda calculus. It expands the standard untyped lambda calculus with types. In typed lambda calculus, supplementary type-checking constrains exclude some forms of expressions. Nevertheless the basic concepts and computation rules are fundamentally the same with the untyped version. 3.1. Syntax of Simply-Typed Lambda Calculus 3.1.1. Terms and Types The only thing that individualises the syntax of the typed lambda calculus from the untyped lambda calculus is that formal variables of abstractions must be provided with their types. 12 Computational lambda calculus The syntax of types for typed lambda terms is given by the grammar W ∷= |1|W( × W) |W′ → W where: • ranges over the base types • 1 is a single type constant representing an atomic type • τ is a type The above grammar means that 1 is a type and if τ 1 and τ 2 are types then W( × W) and W′ → W are also types. The function type W′ → W refers to the functions which get as input objects of type W′, and return as output objects of typeW. Note that → associates right, so Y → Z → [ means Y → Z → [. A simply typed lambda term is defined by the grammar ′ +]' |AF. | ′ ∷=∗ ]* , |F where: ^ • * is of type 1 • AF. is an abstraction of type W → W′ if F is of type W and of type W′ ′ is an application of type W if ′ is of type W′ → W and is of type W • • '^ exists only for = 1 or 2, with both it and *−, −+ having the evident typing and • F is a variable Example 3.1. AF: ` a$. H As in untyped lambda calculus, functions are anonymous. The “AF: ` a$” indicates a function which has F as formal variable of type ` a$ and H is the body of the function. 3.2. Semantics of Simply Typed Lambda Calculus 3.2.1. Typing Rules Typing rules for the typed lambda calculus are expressed in terms of typing judgments which are expressions of the form F( : W( , F) : W) … , Fc : Wc ⊢ : W. This means that if F^ has type W^ , for = 1 … $, then the term is a well-typed term that has type τ. In order to introduce the typing rules for the typed lambda calculus, we have to begin with defining a typing context Γ, which is a finite list of distinct variables, typically written as Γ = F( : W( , F) : W) … , Fc : Wc which means “F^ is of type W^ ”. 13 Computational lambda calculus Unit Γ ⊢∗: 1 Variable 1≤≤$ F( : W( , … , Fc : Wc ⊢ F^ : W^ Binary Products Γ ⊢ ( : W( Γ ⊢ ) : W) Γ ⊢ *W(, W) +: W( × W) Γ, F: W ′ ⊢ : W Γ ⊢ AF. : W′ → W Abstraction Application Γ⊢ (: W → W′ Γ ⊢ Γ ⊢ ( ) : W′ F^ is of type W^ If in a context Γ we have ( of type W( and ) of type W) then *W(, W) + is of type W( × W) If in a context Γ we have F of type W′ and of type τ, then in the same context Γ we have AF. of type W′ → W ): W If in context Γ , is of type W, then is of type W → W ′ and ( ) is of type W′ ( ) Figure 1: Simply typed lambda calculus judgments 3.2.2. α-conversion and α-equivalence α-conversion and α-equivalence is defined in the same way as in untyped lambda calculus. We have to consider though the type of the variable we want to convert. If we want to convert the term fg: h, we have to rename g with a variable of type h. The equality judgement for α-equivalence is given as follows: Γ, F: W ′ ⊢ : W Γ ⊢ AF. = AF ′ . iF′jFk: W′ → W if F ′ : W′ does not appear in Γ Figure 2: α-equality judgement 3.2.3. β-reduction and β-equivalence β-reduction is again defined in the same way as in untyped lambda calculus. Note that in a typed lambda term AF: W. ′, ′ and F must be of type W and of type W′ → W, so we only need substitution i ′jF: Wk to make sure that the term substituted is of the right type. 14 Computational lambda calculus It is important to mention that although untyped lambda calculus is Turing-complete, the simply-typed lambda calculus is not because recursion is not allowed by the typing rules. Simply-typed lambda calculus is strongly normalizing, which means that every sequence of β-reductions terminates to a term in normal form. The equality judgement for β-equality is as follows: Γ, F: W ′ ⊢ : W Γ ⊢ Γ ⊢ AF. ′ ′ : W′ = i ′jFk: W Figure 3: β-equality judgement Another equality judgement of central importance is the one for η-equality and it is shown below. Γ ⊢ : τ → τ′ Γ ⊢ AF. F = : W → W′ Figure 4: η-equality judgement 3.3. Models of Simply Typed Lambda Calculus The simply typed lambda calculus can be modelled in any cartesian closed category like ! and o% !. Definition 3.1. Given a cartesian closed category C, base types B, function symbols f, equations between them, and assignments M for B and f that respect both typing and equations, a model for the simply typed lambda calculus is defined inductively on types by • # is given • #1 = 1, the terminal object of C • #W( × W) = #W( × #W) , defined using the binary product of C • #W → W ′ = #W → #W ′ , defined using the closed structure of C and is defined inductively on terms in context by Variable: #F( : W( , … , Fc : Wc ⊢ F^ : W^ = '^ where '^ is the i-th projection #W( × … #Wc → #W^ Unit: #p ⊢∗: 1 is the unique map from #p to the terminal object 1 of C 15 Computational lambda calculus Binary product: #p ⊢ * ( , ) +: W( × W) : #p → qW( × qW) and qp ⊢ '^ : Wr : qp → qW^ are determined by the finite product structure of C Abstraction: qp ⊢ AF. : W ′ → W: qp → qW ′ → W is given by applying the closed structure of C to the map given by qp, F: W ′ ⊢ : W: #p × qW ′ → qW Application: qp ⊢ ( ) : W′ is determined by the cartesian closed structure of C, specifically by using the binary product structure of #W → W ′ × qW, then post-composition with the evaluation map qW → qW ′ × qW → qW ′ to the maps given by qp ⊢ ( : W → W ′ : qp → qW → W ′ and qp ⊢ ) : W: qp → qW.[3] 16 Computational lambda calculus Chapter 4 Computational Lambda Calculus A function is said to generate a side effect [6] if it changes some state of the system except its output. A change of state can be modifying a global or static variable, writing data to a file or modifying one of its arguments. Side effects of computation are known as computational effects [17] and are used to allow the system to interact with the outside world like the user or the operating system. Computational effects are also used to assist an understandable and concise organization of computations. An example of computational effect is state which allows expression evaluation to interpret and modify the content of a collection of mutable storage cells. Other examples of computational effects are exceptions, input/output, side-effects, nondeterminism, probabilistic non-determinism and continuations. Exceptions and continuations are called control effects. According to Moggi [8], side-effects can be modelled by monads. 4.1. Monads Functional programming languages are divided into two categories. Pure languages are those that do not include any imperative features and side effects, like Haskell, Miranda and Gofer. Impure languages are those languages that expand lambda calculus with some possible effects such as exceptions and assignment. Examples of impure languages are Lisp, Scheme and Standard ML. 17 Computational lambda calculus Both categories have their own advantages and disadvantages. Recent advances in theoretical computer science, particularly in the areas of type theory and category theory, have proposed new approaches using the category theoretic concept of monad to merge pure effects into pure functional languages. Definition 4.1. A monad over a category is a triples, 5, t,, where s: is an endofunctor and 5: 6 s, t: s ) s are two natural transformations where 6 is the identity functor, such that the following diagrams of functors and natural transformations commute The first diagram express the “associative” law for the “multiplication” t, and the second the “identity” law for 5, the “unit” of the monad [20]. The notion of monads offers a suitable framework for generating effects. The connection between monads and computations was first described by Eugenio Moggi in [8] to structure the semantics of computations for programming languages such as ML.. Then Philip Wadler [19] modified Moggi’s proposal and have shown that monadss give an elegant way to structure functional programs which execute naturally imperative operations, for example Haskell The theory of monads arises from category theory and is used to represent computations in terms of values and sequences of computations computations using those values. Monads are very helpful when the programmer needs to perform a functional computation whilst a related computation is carried out "on the side".. In functional programming, the major use of monads is for I/O (input/output) ( operationss and changes in state without making use of language features that bring in side effects.. Even though a function cannot cause a side effect immediately, it can create a value which will be related to a desired side effect that the caller should apply at a suitable time. In imperative programming languages, side effects are embodied in the semantics of the language. Monads allow the programmer to describe describe control flows like exceptions, or to construct procedures that consist of consecutive operations. Also, by determining how joint computations model a different computation, the use of monads absolves 18 Computational lambda calculus the programmer from coding the combination of computations manually whenever it is required. Example 4.1. It is helpful to visualize monads as a scheme for combining computations into other, more difficult computations. As an example, think of the Maybe type in Haskell: data Maybe a = Nothing | Just a This Maybe type corresponds to the type of computations which might fail to output a result. In this constructor, failure corresponds to Nothing and success to Just. If a joint computation comprises of a computation B which relies on the result of a different computation A, then the combined computation must output Nothing when either A or B yield Nothing. The joint computation should apply the result of B to the result of A and return it, once both computations succeed. Haskell is a functional programming language that makes heavy use of monads. Definition of Maybe Monad in Haskell [13] instance Monad Maybe where return a = Just a Nothing >>= f = Nothing Just x >>= f = f x fail _ = Nothing Given the definition: do x <- Just ’a’ y <- Just ’b’ return (x,y) The output is: Just (’a’,’b’) Given the definition: do x <- Nothing :: Maybe Char 19 Computational lambda calculus y <- Just ’a’ return (x,y) The output is: Nothing In this example we can see that the computation failed to output a result because A yields to Nothing whereas in the first example we got an output because both A and B succeeded. Definition of Lists Monad in Haskell [13] instance Monad [] where return x = [x] l >>= f = concat (map f l) fail _ = [] For example if we define: do x ‹– [1,2] y ‹– [3,4] return (x,y) Then as a result we get: [(1,3), (1,4), (2,3),(2,4)] Monads provide modularity to programs and they are particularly helpful for constructing large systems. The big amount of online tutorials shows that the notion of monads is a difficult concept to understand. However, once an understanding is achieved, you can use monads in several different problems. 20 Computational lambda calculus 4.2. Computational Lambda Calculus In lambda calculus, functions differ from those in imperative programming languages. Simply-typed lambda calculus is an effect-free language. A function in an imperative programming language can have side effects. This leads to the action of an expression to be based on the exact state of the program, whereas in lambda calculus, the function gives an answer which is mathematically equivalent to the expression and it is not based on its variables. In imperative programming languages the state of a program is described by its parameters. Many programming languages have both functional and imperative features. One way in which we can systematically study this combination is to modify the “simplytyped lambda calculus” to distinguish between values and computations. This yields what is called the computational lambda calculus; a formal framework, based on a categorical semantics for computations, designed to provide a basis for reasoning about the equality of programs. Computational lambda calculus is a fragment of ML and provides a correct basis for proving equivalence of programs, independent from any specific computational model. It was introduced by Eugenio Moggi in his paper “Computational Lambda Calculus and Monads”[8]. 4.2.1. Syntax of Computational Lambda Calculus The syntax of the λc-calculus as described in [4,9,10] can be considered to be identical to the syntax of the simply-typed lambda calculus. So the syntax of types for computational lambda terms is given by the grammar W ∷= |1|W( × W) |W′ W where: • ranges over the base types • 1 is a single type constant representing an atomic type • τ is a type The syntax for a computational lambda term is defined by the grammar ∷=∗ ]* , ′ +]'^ |AF. | ′ |F where: • * is of type 1 • AF. is an abstraction • ′ is an application • '^ exists only for = 1 or 2 and • F is a variable 21 Computational lambda calculus The computational lambda calculus has two predicates: a predicate for equality (=) and a unary predicate (− ) ↓ for “effect-freeness” or “definedness” which is the only feature of the computational lambda calculus that goes beyond the simply type lambda calculus. There are also few differences in the rules for equality. Effect-freeness is closed under equality and the rules are described as follows: • * ↓ , x ↓ , λx.e ↓ for all e • if e ↓ , then π i (e) ↓ • similarly for e, e' For equality, there are two classes of rules. The first class describes equality as the congruence relation on well-typed terms of the same type under the same context. The second class are rules for the basic constructions and for unit, product and functional types. The rules are closed under substitution of effect-free terms for variables. It follows from the rules of the two predicates that types in conjunction with equivalence classes of terms in context form a category, with a subcategory determined by effect free terms [1]. 4.2.2. Semantics of Computational Lambda Calculus The key aspects for category theoretic models, is that there are two entities, values and expressions. Therefore, the easiest way to represent the language as we have formulated it is in terms of a pair of categories C0 and C1 , together with an identityon-objects inclusion functor J : C 0 → C1 . This leads to the concept of closed Freydcategory [1,2]; a simplification of the concept of a category with finite products which is appropriate for representing environments in call-by-value programming languages, like the computational lambda calculus with computational effects. Although closed Freyd-categories provide the most direct sound and complete models for the computational lambda calculus, there were not the first class given. The first class was introduced by Moggi in [8] and was given by a category C with finite products, in addition with a strong monad M and M-exponentials - specifically, for every object A and B, an exponential (MB)A of MB by A. It is important to mention that computational lambda calculus does not include any computational effects in it. However, it is a well-designed calculus to which we can add them and here is when computational lambda calculus gets interesting. So we consider the example of global state. 22 Computational lambda calculus 4.3. A Leading Example: Global State A signature for global state as explained in [1,2,10,11] is given by basic operations lookup and update with arities: `u: @a` → ? a! : 1 → ? × @a` that update and lookup the state respectively. ? is considered to be a finite set of locations and @a` a countable set of values. These, by identifying @a` with ℵw , ? with its cardinality $ and by allowing substitutions to be applied to occurrences of `u and a! , produce a countable Lawvere theory which is described with details in [2,16]. Usually, a command is considered to be a function from state to state and so if we consider !a! as being the set @a` xyL of functions from locations to values, then `u is represented as the function !a! → !a! z{| → !a! → !a! xyL which given `, }, determines the value of ` by checking in }: ? → @a`. The operation a! is represented as the function !a! → !a! → !a! → !a! xyL×z{| which given `, ~, } replaces the value at ` by ~ at }: ? → @a`. These generate the standard model for global state. John Power in [1] makes use of the countable Lawvere theory mentioned above in conjunction with a model of it in !, in order to construct the seven equations shown below, among pairs of words generated by `u and a! . This yields to a countable Lawvere theory for side-effects. In this set of equations `u corresponds to the symbol ` and a! to the symbol . `|yL |yL, F = F `|yL `|yL ! = `|yL ! 23 Computational lambda calculus |yL, |yL, F = |yL, F |yL, `|yL ! = |yL, ! `|yL `|yL ! = `|yL `|yL ! where ` ≠ `′ |yL, |yL , F = |yL , |yL, F where ` ≠ `′ |yL, `|yL ! = `|yL |yL, ! where ` ≠ `′ Figure 5: Seven equations of global state More details about how this seven equations were derived, including the diagrams are given in [1]. Also a similar example for local state can be found in the same paper. If we ignore lambda terms for a while and consider words generated by `u and a! can we say when two words are equal? As in “The word problem for groups” described in chapter 2 (section 2.3) words generated by `u and a! may look different but actually be equal to each other. It follows from the theorem in [10] that two terms generated by `u and a!e are equal relative to those seven equations if and only if they are equal in the standard model. A question is what would happen if we had a different set of equations? The same result would be true for any set of equations that are equivalent to those seven equations. 24 Computational lambda calculus Chapter 5 Implementation and Testing One of the aims of this project was to implement the simply typed lambda calculus and then modify it into the computational lambda calculus. Unfortunately, I was able to implement only the simply typed lambda calculus and the code is given in appendix A. The programming language chosen for the implementation is Lisp. This is because Lisp is a functional programming language which has lambda calculus as its basis and also it is a programming language I am more familiar with. Black box testing strategy was used in order to test the program. In the table below there are listed several test inputs which will check if the program operates correctly and if we get back the expected output. 5.1. Program Documentation and Testing In Lisp, simply typed lambda terms can be represented using lists. The simply typed lambda calculus has: • type constructor: W ∷= |1|W( × W) |W′ W where in this implementation ranges over a! for natural numbers and ` for truth values and • term constructor: ∷=∗ ]* , ′ +]'^ |AF. | ′ |F Unit Variable Term ∗: 1 F: W Representation ∗ ∶ 1 @ F ∶ W 25 where x is a symbol and W Computational lambda calculus Abstraction AF: W. ′ Application Product * , ′+ ?# F ∶ W oo I , ′K a valid type Where x is a symbol, W a valid type and is another term. Where is the first term and is the second term. Where is the first term and is the second term. Table 1: Representation of simply typed lambda terms In order to be easier later on to implement α-conversion, substitution and βreduction, I started by implementing a number of simple helper functions. In the table below I give an explanation about what each function does and also few test inputs for each one to make sure they work correctly. Test Input Expected Output Pass/Fail Function isVariable This function checks if the given term is a valid variable (isVariable ‘(VAR x : Nat)) (isVariable ‘(VAR x : Bool)) (isVariable ‘(VAR x : B)) (isVariable ‘(VAR x)) #t #t () () Pass Pass Pass Pass Function isAbstraction This function checks if the given term is a valid abstraction (isAbstraction ‘(LAMBDA x : Nat (VAR y : Nat))) (isAbstraction ‘(LAMBDA x (VAR y))) (isAbstraction ‘(VAR y : Nat)) (isAbstraction ‘(LAMBDA x : Nat (LAMBDA y : Bool (VAR z : Nat)))) #t Pass () Pass () #t Pass Pass Function isApplication This function checks if the given term is a valid application (isApplication '(APP(VAR y : Nat)(VAR x : Nat))) (isApplication '(APP(VAR y : Nat)(VAR x))) #t Pass () Pass 26 Computational lambda calculus (isApplication '((VAR x : Bool)(LAMBDA x : Bool (VAR y : Nat)))) (isApplication '(APP(LAMBDA x : Bool (LAMBDA x : Bool(VAR y : Nat)))(LAMBDA x : Bool (LAMBDA x : Bool(VAR y : Nat))))) () Pass #t Pass Function isUnit This function checks if the given term is unit term of the form (* : 1) (isUnit '(* : 1)) (isUnit '(VAR y : 1)) (isUnit '(* : Nat)) #t () () Pass Pass Pass Function isBProduct This function checks if the given term is a valid product (isBProduct '((VAR y : Nat)(VAR x : Bool))) (isBProduct '((VAR y : Nat)(LAMBDA x : Nat (VAR z : Bool)))) (isBProduct '((VAR y : Nat)(LAMBDA x : Nat (VAR z)))) (isBProduct '((VAR y : Nat))) (isBProduct '(* : 1)) (isBProduct'(APP(VAR Y : Nat)(VAR X : Nat))) #t Pass #t Pass () Pass () () () Pass Pass Pass Function var-symbol This function returns the symbol of a variable (var-symbol '(VAR x : Nat)) (var-symbol'(APP(VAR Y : Nat)(VAR X : Nat))) (var-symbol '(VAR x)) x () Pass Pass () Pass Function var-symbol-type This function returns the type of the symbol of a variable (var-symbol-type '(VAR y : Nat)) (var-symbol-type '(VAR y)) (var-symbol-type '(APP(VAR Y : Nat)(VAR X : Nat))) (var-symbol-type '(VAR y : Int)) (var-symbol-type '(VAR y : Bool)) Nat () () Pass Pass Pass () Bool Pass Pass 27 Computational lambda calculus Function make-var This function takes a symbol and a type and returns a representation of a variable using that symbol and type (make-var 'x 'Bool) (make-var 'x 'Nat) (make-var 'x 'int) (VAR x : Bool) (VAR x : Nat) () Pass Pass Pass Function abs-var This function returns the variable symbol of an abstraction term (abs-var '(LAMBDA x : Nat (VAR y : Bool))) (abs-var '(LAMBDA x : Nat (VAR y))) (abs-var '(VAR x : Nat)) (abs-var '(LAMBDA x (VAR y : Bool))) (abs-var '(LAMBDA x : Bool (VAR y : Nat))) (abs-var '(APP(VAR Y : Nat)(VAR X : Nat))) x Pass () Pass () () Pass Pass x Pass () Pass Function abs-var-type This function returns the type of the variable symbol of an abstraction term (abs-var-type '(LAMBDA x : Nat (VAR y : Bool))) (abs-var-type'(VAR y : Bool)) (abs-var-type '(APP(VAR Y : Nat)(VAR X : Nat))) Nat Pass () () Pass Pass Function abs-body This function returns the body of an abstraction term (abs-body '(LAMBDA x : Nat (VAR y : Bool))) (abs-body '(VAR y : Nat)) (VAR y : Bool) Pass () Pass Function make-abs This function takes a symbol with its type and a term and returns a corresponding abstraction term (make-abs 'x 'Bool '(LAMBDA y : Nat (VAR z : Bool))) (make-abs 'x 't '(LAMBDA y : Nat (VAR Z : Bool))) (make-abs 'x 'Nat '(APP(VAR y : Nat)(VAR x : Nat))) (LAMBDA x : Bool (LAMBDA y : Nat (VAR z : Bool))) () (LAMBDA x : Nat (APP (VAR y : Nat) (VAR x : Nat))) 28 Pass Pass Pass Computational lambda calculus Function app-fun This function returns the function term of an application (app-fun '(LAMBDA x : Nat (VAR y : Bool))) (app-fun '(APP(VAR y : Nat)(VAR x : Nat))) (app-fun '(VAR y : Bool)) (app-fun '(APP(LAMBDA x : Bool (LAMBDA x : Bool(VAR y : Nat)))(LAMBDA x : Bool (LAMBDA x : Bool(VAR y : Nat))))) () Pass (VAR y : Nat) Pass () (LAMBDA x : Bool (LAMBDA x : Bool (VAR y : Nat))) Pass Pass Function app-arg This function returns the argument term of an application (app-arg '(APP(LAMBDA x : Bool (LAMBDA x : Bool(VAR y : Nat)))(LAMBDA x : Bool(VAR y : Nat)))) (app-arg'(APP(VAR y : Nat)(VAR x : Nat))) (app-arg '(VAR y : Nat)) (app-arg '(LAMBDA x : Bool (LAMBDA x : Bool(VAR y : Nat)))) (LAMBDA x : Bool (VAR y : Nat)) Pass (VAR x : Nat) Pass () () Pass Pass Function make-app This function takes two terms and returns an application term (make-app '(VAR y : Nat)'(LAMBDA x : Bool(VAR y : Nat))) (make-app '(APP (VAR y : Nat) (LAMBDA x : Bool (VAR y : Nat)))'(LAMBDA x : Bool(VAR y : Nat))) (APP (VAR y : Nat) (LAMBDA x : Bool (VAR y : Nat))) Pass (APP (APP (VAR y : Nat) (LAMBDA x : Bool (VAR y : Nat))) (LAMBDA x : Bool (VAR y : Nat))) Pass Function alphaconvert This function returns a term computed by replacing all free occurrences of the variables (VAR x : t) in term by (VAR y : t) (alphaconvert '(LAMBDA x : Nat (VAR y : Nat)) '(VAR x : Nat)'(VAR Y : Nat)) (alphaconvert '(LAMBDA x : Nat (VAR y : Nat)) '(VAR x : (LAMBDA x : Nat (VAR y : Nat)) Pass (LAMBDA x : Nat (VAR y : Nat)) Fail 29 Computational lambda calculus Nat)'(VAR Y : Bool)) (alphaconvert '(VAR x : Nat)'(VAR y : Nat)'(VAR x : Bool)) (VAR x : Nat) Fail Table 2: Testing table 1 Substitution was implemented in two functions. This is because I always had the same value when I was calling gensym to avoid the capture of free variables. Two new functions were implemented; a helper function called h-subs which does the substitution, and a function subs that will take the terms and the variables needed for the substitution. Subs then calls h-subs to actually perform the substitution after doing some type checking on the given input. By doing this each time the helper function h-subs is called, a new value of gensym is generated for each iteration. Another two helper functions isRedex and contains-redex were implemented in order to implement β-reduction. isRedex checks if the given term is an application and the function term of that application is an abstraction. If this occurs then the term is a redex. The second function contains-redex checks if the given term is an application. If it is then checks if either the term is already a redex or the application function contains a redex or the application argument contains a redex. Else if the given term is an abstraction, it checks if the abstraction body contains a redex. The main function that takes a term and performs one step of β-reduction, using the normal-order reduction strategy, is called normalbeta. In order to be able to perform β-reduction our term has to be/or contain a redex. By using our helper functions we first check if the given term is already a redex or if it’s an application or an abstraction. We are making a new term and invoke recursively our function, that’s how we apply one step of β-reduction to our term. If the given term does not contain a redex then we return the given term. Function isRedex This funcion checks if the given term is a redex or not (isRedex '(APP(LAMBDA x : Nat (VAR y : Bool))(VAR z : Nat))) (isRedex '(LAMBDA x : Nat (VAR y : Bool))) (isRedex '(VAR y : Bool)) #t Pass () Pass () Pass Function contains-redex This function checks if the given term contains a redex (contains-redex '(APP(LAMBDA x : Nat (VAR y : Bool))(VAR z : #t Pass 30 Computational lambda calculus Nat))) (contains-redex '(LAMBDA x : Nat (APP(LAMBDA x : Nat (VAR y : Bool))(VAR z : Nat)))) (contains-redex '(LAMBDA x : Nat (VAR y : Bool))) #t Pass () Pass Function normalbeta This function performs one step of beta reduction (normalbeta '(APP(LAMBDA x : Nat (VAR y : Nat))(VAR z : Nat))) (normalbeta '(LAMBDA x : Nat (APP(VAR x : Nat)(VAR y : Nat)))) (VAR y : Nat) Pass (normalbeta '(LAMBDA x : Nat (APP(VAR x : Nat)(VAR y : Nat)))) Pass Table 3: Testing table 2 The last functions that we had to implement was normal-reduce that computes the normal form of e if it has one using β-reductions in normal. A term is in normal form if it doesn’t contain any redexes. Function normal-reduce This function implements reduction to normal form (normal-reduce '(LAMBDA x : Nat (APP(VAR x : Nat)(VAR y : Nat)))) (normal-reduce '(APP(LAMBDA x : Nat (VAR y : Nat))(VAR y : Nat))) (LAMBDA x : Nat (APP (VAR x : Nat) (VAR y : Nat))) Pass (VAR y : Nat) Pass Table 4: Testing table 3 This testing shows that the majority of the functions work as expected but there are some functions as well which do not give the expected output in certain inputs. In the next chapter we will conclude this project with an overview of the project, evaluation of the results and suggestions of future work that can be done. 31 Computational lambda calculus Chapter 6 Conclusions 6.1. Achievements One of the primary aims of this project was the understanding of computational lambda calculus and computational effects. After a long and careful research I successfully managed to give a description of the concept of computational lambda calculus, computational effects and the important role of monads in programming and computer science in general. Unfortunately the project implementation did not reach the desired standard as I implemented only the simply-typed lambda calculus. To a certain extent, this was because I had to spend more time into researching new topics I had never studied before and I couldn’t start implementation without having a basic understanding of the topic. Category theory was the basis in all the journals I had to read and I was unfamiliar with it. It is a hard topic and took more time than expected in order to obtain the basic idea and especially how it is related with computer science. 6.2. Future Work As an extension of this project can be the design of a calculus that will combine computational lambda calculus with the seven equations for global state in order to check when two lambda terms are equal. The computational lambda calculus does not contain recursion. In studying state, one typically assumes there is a countable set of values. So recursion plays a role here and in order to include it, both the calculus and models need to be extended. 32 Computational lambda calculus 6.3. Personal Reflection Although I didn’t manage to provide a complete implementation, working on this project gave me the opportunity to investigate new areas which I had never studied before and obtain a reasonable understanding of topics that are not usually taught at university. What made the work seem more worthwhile is the fact that this project was one of the 20 projects in the whole UK selected for the BCSWomen Undergraduate Lovelace Colloquium which is poster contest for women students of computing, which took place in Leeds on the 16th of April 2009. I believe that the project was undoubtedly a challenge but something that I was absolutely able to enjoy and gain knowledge of it. 33 Computational lambda calculus Bibliography [1] A.J. Power, Canonical models for computational effects, Proc. of FOSSACS 2004, Lecture Notes in Computer Science, Vol. 2987, pp. 438-452, 2004. [2] A.J. Power, Generic models for computational effects. Theoretical Computer Science, Vol. 364, p.254-269, 2006. [3] A.J. Power, CM30071: Logic and its applications lecture notes, University of Bath, 2009. [4] A.J. Power, Models of the computational lambda calculus, Proceedings MFCSIT 2000, ENTCS, Vol. 40, 2001 [5] B.C. Pierce, Basic Category Theory for Computer Scientists. MIT Press, 1991. [6] B. Harvey, M. Wright, Simply Scheme: Introducing Computer Science, p. 343349, The MIT Press, Cambridge, London, England, 1994. [7] C. Hayashi, The word problem for groups with regular relations: improvement of the Knuth-Bendix algorithm, Publications of the Research Institute for Mathematical Sciences, 1992. [8] E. Moggi, Computational lambda-calculus and monads, Proceedings of the Fourth Annual Symposium on Logic in computer science, p.14-23, Pacific Grove, California, United States, 1989. [9] G.D. Plotkin, A.J. Power, Logic for Computational Effects: Work in Progress, 6th International Workshop on Formal Methods, Dublin City University, Ireland, 2003. [10] G.D. Plotkin, A.J. Power, Notions of Computation Determine Monads, Proceedings of the 5th International Conference on Foundations of Software Science and Computation Structures, p.342-356, 2002 [11] G.D. Plotkin, A.J. Power, Tensors of Comodels and Models for Operational Semantics, Electronic Notes in Theoretical Computer Science 218, p.295-311, 2008 34 Computational lambda calculus [12] G. Mazzola, G. Milmeinster, J. Weissmann, Comprehensive Mathematics for Computer Scientists 2,p. 313-332, Springer, Berlin, Heidelberg, 2005. [13] H. Daume, Online Haskell tutorial, Source: http://darcs.haskell.org/yaht/yaht.pdf [14] J.C. Mitchell, Concepts in Programming Languages, p 57-67, Cambridge University Press, UK, 2003. [15] K.B. Bruce, Foundations of Object-Oriented Languages: Types and Semantic, p.120-140, The MIT Press, Cambridge, London, England, 2002. [16] M. Hyland , G. Plotkin , A.J. Power, Combining effects: sum and tensor, Theoretical Computer Science, Vol.357, p.70-99, 2006. [17] P.B. Levi, Call-By-Push-Value: A Functional/Imperative Synthesis, Kluwer Academic Publishers, Netherlands, 2003. [18] P.L. Curien, Categorical Combinators, Sequential Algorithms and Functional programming, Pitman, 1986. [19] P.Wadler, Monads for functional programming, Lecture Notes In Computer Science; Vol. 925, p.24 – 52, Springer-Verlag, London, UK, 1995. [20] S. Mac Lane, I. Moerdijk, Sheaves in Geometry and Logic: A first Introduction to topos theory, Springer, New York, 1992. 35 Computational lambda calculus Appendix A Code ;this is a function that checks if the given term ;is a variable of type Bool for boolean and Nat for natural numbers (defun isVariable (term) (if (eq (length term) 4) (if (and (eq 'VAR (car term))(symbolp (cadr term))(eq ': (caddr term)) (or(eq 'Bool (cadddr term))(eq 'Nat (cadddr term)))) #t '() ) ) ) ;this is a recursive function that checks if the given term is an abstraction ;of the form (LAMBDA x:T term) where T is the type (boolean or natural number) (defun isAbstraction (term) ;check if the length of the given list is 5 (if (eq (length term) 5) (if (and (eq 'LAMBDA (car term))(symbolp (cadr term))(eq ': (caddr term))(or(eq 'Bool (cadddr term))(eq 'Nat (cadddr term)))) (or (isVariable (car (cddddr term)))(isAbstraction (car (cddddr term))) (isApplication (car (cddddr term)))) '() ) '() ) ) ;this is a recursive function that checks if the given term is an application ;of the form (APP term1 term2) (defun isApplication (term) ;check if the length of the given list is 3 36 Computational lambda calculus (if (eq (length term) 3) (if (eq 'APP (car term)) (and (or (isVariable (caddr term)) (isApplication (caddr term)) (isAbstraction (caddr term))) (or (isVariable (cadr term)) (isApplication (cadr term)) (isAbstraction (cadr term)))) '() ) '() ) ) ;this is a function that checks if the given term is a unit of type 1 (*:1) (defun isUnit (term) (if (eq (length term) 3) (if (and (eq '* (car term))(eq ': (cadr term))(eq '1 (caddr term))) #t '() ) '() ) ) ;(term1 term2) (defun isBProduct (term) ;check if the length of the term is 2 (if (eq (length term) 2) (and (or (isVariable (car term)) (isApplication (car term)) (isAbstraction (car term))) (or (isVariable (cadr term)) (isApplication (cadr term)) (isAbstraction (cadr term)))) '() ) ) ;this is a function that returns the symbol from a variable term (defun var-symbol (term) (if (isVariable term) (cadr term) '() ) ) ;this is a function that returns the type of the symbol from a variable term (defun var-symbol-type (term) (if (isVariable term) (cadddr term) '() ) 37 Computational lambda calculus ) ;this function takes a symbol and type and returns the representation of ;a variable using that symbol and type (defun make-var (symbol type) (if (and (symbolp symbol) (or (eq 'Bool type)(eq 'Nat type))) ;build a new list with the symbol and type prefixed by var (cons 'VAR (cons symbol (cons ': (cons type '())))) '() ) ) ;this is a function that returns abstraction term (defun abs-var (term) (if (isAbstraction term) (cadr term) '() ) ) the variable symbol of an ;this is a function that returns the type of the variable symbol of an abstraction term (defun abs-var-type (term) (if (isAbstraction term) (cadddr term) '() ) ) ;this is a function that returns the body of an abstraction term (defun abs-body (term) (if (isAbstraction term) (car(cddddr term)) '() ) ) ;this is a function that takes a symbol with its type and a term and returns a ;corresponding abstraction term (defun make-abs (symbol type term) ;checks if the given symbol and term are valid (if (and (symbolp symbol) (or (eq 'Bool type)(eq 'Nat type)) (or (isVariable term) (isAbstraction term) (isApplication term))) ;build a new list with the symbol and term prefixed by lambda (cons 'LAMBDA (cons symbol (cons ': (cons type (cons term '()))))) 38 Computational lambda calculus '() ) ) ;this is a function that returns the function term of an application (defun app-fun (term) (if (isApplication term) (cadr term) '() ) ) ;this is a function that returns the argument term of an application (defun app-arg (term) (if (isApplication term) (caddr term) '() ) ) ;this is a function that takes two terms and returns an application term (defun make-app (term1 term2) ;checks if the given terms are valid (if (and (or (isVariable term1) (isAbstraction term1) (isApplication term1)) (or (isVariable term2) (isAbstraction term2) (isApplication term2))) ;build a new list with the two terms prefixed by app (cons 'APP (cons term1 (cons term2 '()))) '() ) ) ;this is a helper function for substitution. We need it ;so the random variable could change in each iteration (defun h-subs (term1 var term2 randomvar) (cond ;if the variable that we want to replace (var) is the same as term1 ((and (isVariable term1) (eq (var-symbol var) (varsymbol term1)) (eq (var-symbol-type var) (var-symboltype term1))) ;then return term2 term2 ) ;if term1 is an application ((isApplication term1) ;we alphaconvert the application 39 application function and Computational lambda calculus ;argument and perform a substitution.We make a new application (make-app (h-subs (app-fun term1) var term2 (gensym)) (h-subs (app-arg term1) var term2 (gensym))) ) ((and (isAbstraction term1) (not (eq(abs-var term1) (var-symbol var))) (eq (var-symbol-type var) (abs-var-type term1))) ;we substitute through the body of the abstraction and ;then alphaconvert it. We make a new abstraction (make-abs randomvar (var-symbol-type var) (h-subs (alphaconvert (abs-body term1) (abs-var term1) randomvar) var term2 (gensym))) ) (#t term1) ) ) ;this is a function for a-conversion of simply typed lambda terms.It returns a term computed ;by replacing all free occurences of the variables (VAR x : t) in term by (VAR y : t) (defun alphaconvert (term varx vary) ;check if the two variables have the samen type (if (and (isVariable varx) (isVariable vary) (eq (var-symbol-type varx) (var-symbol-type vary))) ;checks if the given term and valid (if (or (isVariable term) (isAbstraction term) (isApplication term)) (cond ((and (isVariable term) (eq (var-symbol term) (var-symbol varx)) (make-var (var-symbol vary) (var-symbol-type vary)))) ((isAbstraction term) (if (and (eq (abs-var term) (varsymbol varx)) (eq (var-symbol-type varx) (abs-var-type term))) term ;invoke the function again on the body of the abstraction (make-abs (abs-var term) (alphaconvert (abs-body term) varx vary)) ) ) ;if it's an application then the invoke the function to the ;function term and the argument term of the application 40 Computational lambda calculus ((isApplication term) (make-app (alphaconvert (app-fun term) varx vary) (alphaconvert (app-arg term) varx var y))) ;if we don't match any condition above - then we can't ;alphaconvert the term, so just return it (#t term) ) '() ) ) ) ;this function implemets substitution.If we have two abstractions ;term1, term2 and a variable var ;then (subs term1 var term2) computes a representation ;of the term term1[term2/var] (defun subs (term1 var term2) (if (and (or (isVariable term1) (isAbstraction term1) (isApplication term1)) (isVariable var)) (h-subs term1 var term2 (gensym)) '() ) ) ;this is a function that checks if the given term ;is a redex or not (defun isRedex (term) (if (and (isApplication term)(isAbstraction (app-fun term))) #t '() ) ) ;this is a function that checks if the given term ;contains a redex (defun contains-redex (term) (if (isApplication term) ;check if : ;either term is already a redex ;the application function contains a redex ;or the application argument contains a redex (or (isRedex term) (contains-redex (app-fun term)) (contains-redex (app-arg term))) ;else if the given term is an abstraction (if (isAbstraction term) ;take a look if the abstraction body contains a redex (contains-redex (abs-body term)) '() ) ) 41 Computational lambda calculus ) ;this function performs one step of beta reduction (defun normalbeta (term) (if (or (isVariable term) (isAbstraction term) (isApplication term)) (if (contains-redex term) (cond ;if the term is a redex we apply the substitution ((isRedex term) (subs (abs-body (app-fun term)) (make-var (abs-var (app-fun term)) (abs-var-type (app-fun term))) (app-arg term))) ;make a new abstraction and perform one step of beta reduction ;to the abstraction body ((isAbstraction term) (make-abs (abs-var term) (abs-var-type term) (normalbeta (abs-body term)))) ;if the application argument contains the redex ((isApplication term) (if (isRedex (app-arg term)) ;make a new application and perform one step of beta reduction ;to the application argument (make-app (app-fun term) (normalbeta (app-arg term))) ;perform one step of beta reduction ;to the application function and make a new application (make-app (normalbeta (app-fun term)) (app-arg term)) ) ) ) term ) '() ) ) ;this is a function that implements reduction to normal form (defun normal-reduce (term) (if (contains-redex term) (normal-reduce (normalbeta term)) term ) ) 42