A mathematical model for our discussion.


We shall regard computation as taking place in a countably infinite universe of objects. This universe is created from a certain set of base objects by using a single function, Apply. The universe contains at least the following base objects

Types of Object

A type T is a subset T<=Universe. In particular there are the following base types The (non-base) type Rational = Integer \/ Ratio The (non-base) type Number = Rational \/ Float

These base-types are mutually exclusive - any object belongs to at most one base-type.

The MayBe and CouldBe type constructors.

Let T <= Universe be a type of which false is not a member. Then MayBe(T) denotes the type
T \/ {false}

Let T <= Universe be a type. Then CouldBe(T) denotes the type T\/Exception.

Function Types

Definition

If D and R are types then D->R is the type
    {p | p of Procedure & !d. d of D ==> (Apply p d) of R }

Thus if p is a procedure, we say that p has the type D->R if p of D->R. It should be noted that in general, no unique type can be ascribed to a procedure.

Unfortunately, it is not practicable to insist that our functions run without exception. Consider the problem with division. Division by zero raises an exception. We might think that we could elaborate our type-system to avoid the possibility of well-typed expressions raising an exception, but this can rapidly be seen to be impossible - consider the rational function:

                 P[x]
    R[x]   =   --------
                 Q[x]

to know whether R[x] will raise an exception, we have to know the zeros of the polynomial Q[x], which is a-trivial problem (and indeed in a certain sense, insoluble) problem. Even if we relaxed our requirement to "f(a) evaluates without exception almost everywhere in A", we still have problems, since to know that this statement is true of R[x] requires knowledge, if not of the Fundamental Theorem of Algebra, at least of the fact that a polynomial can have at most n roots, which, if not exactly a profound theorem, is certainly beyond the scope of knowledge that it is reasonable to expect a type-system to possess.

    /  : Rational->Rational->CouldBe(Rational)

The following basic lemmas hold for function types.

Lemma (contravariance of domain)

If D1, D2, R are types for which D1<=D2, then D2->R <= D1->R

Proof

Let p of D2->R. Then Apply(p d2) of R for all d2 of D2. But D1<=D2. Hence for all d1 in D1, Apply(p,d1) of R. Hence result.

Lemma (covariance of range)

If D, R1,R2 are types for which R1<=R2, then D->R1 <= D->R2

Proof

Let p of D->R1. Then Apply(p,d) of R1 for all d of D. But R1<=R2 Hence Apply(p,d) of R2 for all d of D.

A consequence of the contravariance rule is that, for example, despite the fact that Integer<=Number, the type Integer->Integer is not contained in the type Number->Number. Thus the union (Integer->Integer) \/ (Number->Number)

is non-redundant.

A simple converse of these two lemmas is clearly not true of all domains. for consider a domain without procedure objects. Then D->R is always the empty-set, so that D1->R1<=D2->R2 is true for any domains and ranges whatever. So, discussion of the converse of these lemmas will have to wait until we have established how richly populated our Procedure type needs to be.

Lemma

Let p of D1->R1\/D2->R2...Dn->Rn. Let d in D1/\D2..Dn. Then (Apply p d) of R1/\R2...Rn.

Proof

For all i of 1..n, d of Di. Now p of Di->Ri. Hence (Apply p d) of Ri for all i of 1..n.

This lemma suggests how we can ascribe a type to a function-application, where the function belongs to a type-union of the above kind.

Lemma

(D1\/D2) -> (R1/\R2) = (D1->R1)/\(D2->R2).

Let p of (D1\/D2) -> (R1/\R2). Then, for all d1 in D1, (Apply p d1) of (R1/\R2), so (Apply p d1) of R1. Hence p of (D1->R1) Likewise for all d2 in D2, (Apply p d2) of R2. Hence p in (D1->R)/\(D2->R).

Conversely, let p of (D1->R1)/\(D2->R2). Consider d in (D1\/D2).

If d in D1, (Apply p d) of R1, since p of (D1->R1). Likewise if d in D2, (Apply p d2) of R2. Hence p of (D1\/D2)->(R1/\R2).

Henceforward, we shall adopt the convention that juxtaposition denotes application. Thus if p is a procedure object, and x is an object, then p x denotes Apply(p,x).

We can now complete the definition of our universe.

There are 3 procedure objects S K I, for which the following conditions hold:

+ : (Number->Number->Number)  \/ (Integer->Integer->Integer) \/
    (Rational->Rational->Rational) \/ (Float->Float->Float)

- : (Number->Number->Number)  \/ (Integer->Integer->Integer) \/
    (Rational->Rational->Rational) \/ (Float->Float->Float)


* : (Number->Number->Number)  \/ (Integer->Integer->Integer) \/
    (Rational->Rational->Rational) \/ (Float->Float->Float)


/ : (Rational->Rational-> (CouldBe Rational))
        \/ (Number->Number->(CouldBe Number)

For any types T1,T2 cons : T1 -> T2 -> Prod(T1,T2) front: Prod(T1,T2) -> T1 back: Prod(T1,T2) -> T2

Types of the Shonfinkel Combinators

Lemma

If T is a type, then I:T->T

Proof

.

Let t in T. Then I t = t. Thus I in T->T.

Lemma

If T1 and T2 are types, then K in T1->T2->T1

Proof

. Let t1 in T1, consider (K t1). For all t2 in T2, (K t1) t2 = t1. Thus (K t1) in T2->T1. Hence K in T1->(T2->T1)

Lemma

If T1, T2 and T3 are types, and f in T1->T2->T3, g in T1->T4, where T4<=T2, then S in (T1->T2) -> (T1->T4) -> T1 -> T3

Proof

.

We have to show that if f in T1->T2 then S f in (T1->T4)->T1->T3

Consider S f, and let g in T1->T4 - we have to show that S f g in T1 -> T3.

Consider S f g - and let x in T1 - we have to show that S f g x in T3.

But S f g x = (f x) (g x). Since X in T1, and f in T1->T2->T3, it follows that (f x) in T2->T3. Likewise (g x) in T4. But T4<=T2, so that ((f x) (g x)) in T3. Hence (S f g x) in T3, hence (S f g) in T1->T3, hence S f in (T1->T4)->T1->T3, hence S in (T1->T2)->(T1->T4)->T1->T3.

Terms and their Denotation

In this section we will show how we can characterise any computation as evaluating an expression in the Universe. An expression can consist of

Now one question arises "what does a variable standing by itself mean?". Generally, we think of variables as standing for something else. It is reasonable therefore to require our expressions to contain only bound variables.

First let us consider abstractions. We would like the abstraction \x.(+ x 3)

to "mean" the function "add 3". so that (\x.(+ x 3) 7) ==> 10

Thus we get to understanding the meaning of the application of a lambda-abstraction to arguments in terms of substitution. However we shall adopt a different approach here, motivated by the fact that the concept of a variable and its scope can be tricky to handle.

We define a denotation mapping from Universe->Universe as Den[(\v.v)] = I Den[(\v.u)] = (E `free variable`) Den[(\v.c)] = K c Den[(e1 e2)] = (S Den[e1] Den[e2])

Any object O in Universe can play the role of being a term. Let us define a denotation function Den:Universe->Universe as follows: Den(\x.x) = I Den(\x.c) = K c Den(\x. e1 e2) = S (Den e1) (Den e2)

Lemma

If D1, D2, R1, R2 are types, and D1 is a recognisable type, with recogniser is_D1, then D1->R1 <= D2->R2 ==> D2<=D1 and R1<=R1.

Proof

. Suppose D2 is not included in D1. Then there is a d2 in D2, d2 not in D1. Consider f = \x. if (is_D1 x) instance(D1)

Environments and Evaluation.

An environment is a representation of a finite mapping from symbols to objects.

Eval (f a) e =
    Apply((Eval f e)  (Eval a e)


Eval (if x y z) e = if (Eval x e) then (Eval y e) else (Eval z e)

Eval c  = c

Eval (\x. t)  e  = (Closure x t e)

Eval (Closure x t e) = (Closure x t e)

Eval (quote t) e = t


Apply p x e                   = PrimApply p x
Apply (Closure x t1 e1) t2 e2 = Eval t1  e1[x|t2]