We do not want to be prescriptive about a particular universe. For a start we are not taking a reductionist approach, in which a universe is constructed from an ultra-simple base. For, while a reductionist approach is intellectually satisfying, it fails to address the engineering realities of the construction of any programming language, namely that any programming language is implemented on an existing computer which will offer a range of quite complex capabilities. Moreover, since it is desirable for a language to be implementable on a range of computers, it is better that it be specified in terms of a relatively high-level universe, which can then be mapped to a specific computer.
Given that we expect to incorporate some quite high-level operations in our universes, it is useful to avoid prescribing exactly what they are, beyond the minimum required to achieve a computational capability. For different programming languages can be considered as defined on different basis of primitive operations.
[Note for the html form of the document - we shall write <= for set inclusion, \/ for set-union and /\ for set intersection. We shall also use == to denote an equivalence operation]
If the set E is empty, we say that U is a universe without exceptions.
Remark: the above axioms are characteristic of an eager language. For under lazy evaluation, the argument of (K k) x would not be evaluated, so there would be no way of discovering if it were an exception.
That is, the equivalence relation is the symmetric and transitive closure of t1->t2 iff t1 can be converted into t2 by substituting an instance of the left-hand-side of F1 or F2 by the corresponding instance of its RHS.
Let us denote by t' the equivalence class to which a given term t belongs.
So, it seems that we have a countably infinite model of a computational universe. It should be noted that this is not a constructive algebra in the sense that we can decide whether any two members are equal - the word problem is insoluble. However it does mean that we can go ahead with a clear conscience and prove a few things about computational universes without the agonising suspicion that we are talking about nothing.
[??? this is flawed - we have to allow that S may crash on non-exception arguments, cos we can render the Y-combinator in SK terms. So we need some concept of ]
In general we are interested in being able to prove properties of expressions in a given computational universe. In particular, we will be anxious to know in what circumstances an exception can arise. While proving that an exception can never occur is a hard problem, we can quite easily predict circumstances in which an exception will occur.
Following the standard approach of computer science, we introduce the notion of types of object.
To see the difficulty of proving that an exception can never occur, consider the exception raised by division by zero. 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 non-trivial (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.
We denote the type of procedures of U by Proc(U), and of exceptions by Exc(U).
Thus if p is a procedure, we say that p has the type D->R if p in D->R. It should be noted that in general, no unique function-type can be ascribed to a procedure. For a given procedure object p it is possible that p in D1 -> R1 and also p in D2 -> R2 where there is way of subsuming these two relationships in the form of p in D3 -> R3 without losing useful discriminatory power. For example, the absolute value function in most first-generation Lisp derivative languages belongs to the following function-types:
If D1, D2, R1, R2, are types for which D2<=D1 and R1<=R2, then D1->R1 <= D2->R2
Let p in D1->R1. Then (p d1) in R1 for all d1 in D1.
But D2<=D1. Hence for all d2 in D2, (p d2) in R1. Since however R1 <= R2, we see that (p d2 in R2 .
Thus we have shown that any p in D1->R1. maps D2 to R2, thus establishing our lemma.
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 inteersection
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.
Let t in T. Then I t = t. Thus I in T->T.
It is sufficient to show that if f in T1->T2->T3 then (S f) in (T1->T'2)->T1->T3
Hence it suffices to show that if, in addition, g in T1->T'2 then (S f g) in T1 -> T3.
Hence it suffices to show that if, in addition, x in T1 then 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 T'2. But T'2<=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->T'2)->T1->T3, hence
One interpretation of the above results is that a particular type for one of the combinators is as an intersection. For example, for any two types T1 and T2:
The following lemma suggests how we can ascribe a type to a function-application, where the function belongs to a type-intersection.
For all i of 1..n, d of Di. Now p of Di->Ri. Hence (p d) of Ri for all i of 1..n.
From the covariance-contravariance lemma, we can readily infer the following.
There is an element 0 in Integer
There is an element 1 in Integer
There is an element + in Integer->Integer->Integer with the property that for all x,y,z in Integer
There is an element negate in Integer->Integer with the property that (+ (negate x) x) = 0
There is an element * in Integer->Integer->Integer with the property that for all x,y,z in Integer
For all x,y,z in Integer, (* x y) = (* x z) implies y = z or x=0
Integer is generated by 0,1,+,*,negate
Let us now consider the problem of embedding the rationals in a universe which already has embedded integers. We could go about this in two ways. The rationals could be made disjoint from the integers (which would be the way it would be done in SML), or they can be made to include the integers (as in Common Lisp, Scheme, POP-11, Mathematica). A universe U is said to have the rationals embedded if there is a type Rational<=U for which
There is an element 0 in Rational
There is an element 1 in Rational
There is an element + in Rational->Rational->Rational with the property that for all x,y,z in Rational
There is an element negate in Rational->Rational with the property that (+ (negate x) x) = 0
There is an element * in Rational->Rational->Rational with the property that for all x,y,z in Rational
There is an element reciprocal in Rational->Rational->MayFail(Rational,e_div) with the property that for for all x/=0 in Rational (* (reciprocal x) x) = 1
Rational is generated by 0,1,+,*,negate,reciprocal
There is no sequence x0...xn for which x0 = 1 = xn and xi = (* a xi-1), a/=0
Clearly the concept of type-equivalence defined above is an equivalence relation.
Clearly, if we have two members u1,u2 of a Hindley-Milner Universe U, we can choose two terms t1,t2 with distinct type variables such that u1 = t1' and u2 = t2'.
well-defined....
Consider (v1->v2->v1)' in U, where v1,v2 are distinct type-variables. Let t1' t2' in U with type-variables distinct from v1,v2. Then
Consider S = ((v1->v2->v3) -> (v1->v2) -> v1 -> v3)'
Let f', g' and x' be in U.
If sf = mgu(f,(v1->v2->v3) then (S f) = (sf((v1->v2) -> v2->v3))'
if sg = mgu(g,sf(v1->v2)) then (S f g) = (sg (v1->v3))'
if sx = mgu(x,sg(v1)) then (S f g x) = (sx(v3))'
Otherwise, if any of the most-general-unifiers does not exist,
(S f g x) in E.
Now consider ((f' x') (g' x')). f = sf(v1->v2->v3), x = sx(sg((v1)) If this does not evaluate to e, then there exist t_D and t_R for which (f' x') = (t_D -> t_R)' s = mgu(t_D,g)