These base-types are mutually exclusive - any object belongs to at most one base-type.
Let T <= Universe be a type. Then CouldBe(T) denotes the type T\/Exception.
{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.
If D1, D2, R are types for which D1<=D2, then D2->R <= D1->R
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.
If D, R1,R2 are types for which R1<=R2, then D->R1 <= D->R2
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.
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.
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
Let t in T. Then I t = t. Thus I in T->T.
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.
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)
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]