Download Computational lambda calculus: A combination of functional and

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

Intuitionistic type theory wikipedia , lookup

C Sharp (programming language) wikipedia , lookup

Closure (computer programming) wikipedia , lookup

Currying wikipedia , lookup

Standard ML wikipedia , lookup

Anonymous function wikipedia , lookup

Curry–Howard correspondence wikipedia , lookup

Lambda lifting wikipedia , lookup

Lambda calculus wikipedia , lookup

Lambda calculus definition wikipedia , lookup

Combinatory logic wikipedia , lookup

Transcript
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