v: ref int;just like:
l: list int;declares l to be a list of integers.
:= : ∀ T . (ref T × T) -> voidThe first defining assignment, so, using the definition of v above, we can say:
! : ∀ T . ref T -> T
v := 10;(using a suitable infix notation), and the second defining dereferencing, so we can also say:
x: int;(using a prefix notation).
x = !v;
identity |
S ≤ S |
is always true |
lists |
list S' ≤ list S |
iff S' ≤ S |
pairs |
(S', T') ≤ (S, T) |
iff S' ≤ S & T' ≤ T |
records |
{a: S', b: T', c: U, etc} ≤ {a:S,
b:T} |
iff S' ≤ S & T' ≤ T |
functions |
(S' -> T') ≤ (S -> T) |
iff S ≤ S' & T' ≤ T |
etc |
ref S' ≤ ref S iff S' = Swhich is not particularly useful (except in that it is at least safe!).
ref T ≡ (T -> void, void -> T)and assignment and dereferencing could be implemented by:
:= (v, x) = (first v)(x)(if the two accessor functions shared access to a variable in a common scope, then this could actually work! But not as efficiently as a conventional implementation.)
! (v) = (second x)( )
ref[S => T] ≡ (S -> void, void -> T)(with the consistency constraint that S ≤ T)
ref[S' => T'] ≤ ref[S => T] iff S ≤ S' & T' ≤ Tjust like the rule for functions.
We need to introduce two more, somewhat special types:There are some useful coercions for these extended refs, which aren't possible with conventional variables:
- ^ is the top type
- Top is the type containing all values (i.e. the type whose value set is the universal set). Top is the top element in the type lattice, and every other type is a subtype of top, i.e. ∀ T . T ≤ ^. Thus, wherever a value of type top is required, any other value can be given. However there are no operations on values of type top, so there isn't very much useful that can be done with it! Top can also be thought of as the union of all other types:
^ = ∀ T . TTop is usually represented by a symbol which is an upside-down bottom (⊥), but this is not in HTML's repertoire, so I'm using '^' here instead!
- ⊥ is the bottom type
- Bottom is the type that has no values (i.e. the type whose value set is the empty set). Since there are no values with the type bottom it is not possible to call a function that has a parameter type of bottom. Bottom is often used to represent the value returned by the non-terminating computation, but it has a possibly more respectable definition as:
⊥ = ∃ T . TBottom is the bottom element in the type lattice, and a subtype of every other type, i.e. ∀ T . ⊥ ≤ T
make_readonly: ref[S => T] -> ref[⊥ => T]ref[⊥ => T] is the type of a read-only variable, since the only values that can be assigned to it are values of the type bottom, and there are no such values, and ref[S => ^ ] is the type of a write-only variable, since dereferencing the variable returns a value of type top, which cannot be manipulated.
make_writeonly: ref[S => T] -> ref[S => ^ ]
ref[S => T] ≤ ref[⊥ => T] ∀ S, since ⊥ ≤ Sand:
ref[S => T] ≤ ref[S => ^ ] ∀ T, since T ≤ ^So that wherever a read-only or write-only reference is needed, it is always possible to supply a read-write reference.
ref[S => T'] ≤ ref[S => T] iff T' ≤ Tand the special case for read-only references is:
ref[⊥ => T'] ≤ ref[⊥ => T] iff T' ≤ TMy thesis showed how this can be used to provide a self reference when implementing objects.
ref[S => T]that satisfies the 'consistency constraint', i.e. S ≤ T, and:
ref[S => T] ≤ ref[S' => T']then:
i.e., the consistency constraint on the ref with the supertype is automatically satisfied. This means that substituting a valid variable into a context where a more general variable was expected will still be a valid variable. A very useful property! Since otherwise we would have to do a run-time check to check the consistency constraint whenever a compatible subtype was used. Since all variables are created with well-known types that can be checked, this means that all other variable bindings that can actually be used will automatically be consistent - those that are not consistent will never find a real variable that is type compatible with them!S' ≤ T' (since S' ≤ S ≤ T ≤ T')