The subtype refinement relation
Instructor's Guide
intro,
inheritance,
subtypes,
polymorphism,
types,
hiding,
self-reference,
summary,
Q/A,
literature
In determining whether a given type is a subtype of another type, we must make
a distinction between simple (or basic) types built into the language
and compound (or user-defined) types explicitly declared by the programmer.
Compound types, such as integer subranges, functions,
records and variant records, themselves make use
of other (basic or compound) types.
Basic types are (in principle) only a subtype of themselves,
although many languages allow for an implicit subtyping relation
between for example integers and reals.
The rules given in slide [9-subtypes]
characterize the subtyping relation for the
compound types mentioned.
slide: The subtype refinement relation
We use the relation symbol
to denote the subtype relation.
Types (both basic and compound) are denoted by
and
.
For subranges, a given (integer) subrange
is a subtype
of another subrange
if
σ is (strictly) included in
σ
as a subset.
In other words, if
σ = n′..m′ and
τ = n..m then the subtyping
condition is
τ = n..m and
m′\leqslant m.
We may also write
m′\leqslant m in this case.
For functions we have a somewhat similar rule,
a function
f′: σ′→ τ′ (with domain
f′: σ′→ τ′
and range or codomain
τ′) is a subtype of a function
f : σ→ τ (with domain
f : σ→ τ and codomain
τ)
if the subtype condition
τ and
τ′\leqslant τ
is satisfied.
Note that the relation between the domains is contravariant,
whereas the relation between the ranges is covariant.
We will discuss this phenomenon of contravariance below.
Records may be regarded as a collection of
labels (the record fields) that may have values of
a particular type.
The subtyping rule for records expresses that a given record
(type)
may be extended to a (record) subtype by adding
new labels, provided that the types for labels which
occur in both records are refined in the subtype.
The intuition underlying this rule is that by extending a record
we add, so to speak, more information concerning
the individuals described by such a record,
and hence we constrain the set of possible elements
belonging to that (sub)type.
Variants are (a kind of) record that leave the choice
between a (finite) number of possible values, each
represented by a label.
The subtyping rules for variants states that we may
create a subtype of a given variant record if we
reduce the choice by eliminating one or more
possibilities.
This is in accord with our notion of refinement
as improving our knowledge, since by reducing
the choice we constrain the set of possible individuals
described by the variant record.
The subtyping rules given above specify what
checks to perform in order to determine whether
a given (compound) type is a subtype of another type.
In the following we will look in more detail at
the justification underlying these rules, and also hint
at some of the restrictions and problems implied.
However, let us first look at some examples.
See slide [9-ex-subtyping].
- 8..12 → 3..5 \leqslant 9..11 → 2..6
- {age : int, speed : int, fuel : int} \leqslant {age : int, speed : int}
- [ yellow ∨blue ] < [ yellow ∨blue ∨green ]
slide: Examples of subtyping
As a first example,
when we define a function
- 8..12 → 3..5 \leqslant 9..11 → 2..6
- {age : int, speed : int, fuel : int} \leqslant {age : int, speed : int}
- [ yellow ∨blue ] < [ yellow ∨blue ∨green ]
and a function
f : 9..11 → 2..6 then, according to our rules,
we have
f : 9..11 → 2..6.
Recall that we required subtypes to be compatible
with their supertypes, compatible in the sense that an instance
of the subtype may be used at all places where an instance
of the supertype may be used.
With regard to its signature, obviously,
f′ may be used everywhere
where f may be used, since
f′ will deliver a result
that falls within the range of the results expected
from f and, further, any valid argument for f will
also be accepted by
f′ (since the domain of
f′ is larger, due contravariance, than the domain of f).
As another example, look at the relation between
the record types
f′ and
{age : int, speed : int}.
Since the former has an additional field fuel
it delimits so to speak the possible entities falling under
its description and hence may be regarded as a subtype
of the latter.
Finally, look at the relation between the variant records
{age : int, speed : int} and
[yellow : color ∨blue : color ∨green : color ].
The former leaves us the choice between the colors
yellow and blue, whereas the latter also allows
for green objects
and, hence, encompasses the set associated with
[yellow : color ∨blue : color ∨green : color ].
Contravariance rule
The subtyping rules given above are all rather intuitive,
except possibly for the function subtyping rule.
Actually, the contravariance
expressed in the function subtyping rule is somewhat of an embarrassment
since it reduces the opportunities for specializing functions
to particular types.
See slide [9-functions].
[yellow : color ∨blue : color ∨green : color ]
slide: The function subtype relation
Consider, for example, that we have a function
[yellow : color ∨blue : color ∨green : color ], then it seems quite natural to specialize
this function into a function
[yellow : color ∨blue : color ∨green : color ]
(which may make use of the fact that Nat only contains
the positive elements of Int).
However, according to our subtyping rule
[yellow : color ∨blue : color ∨green : color ],
since the domain of
f′ is smaller than the
domain of f.
For an intuitive understanding of the function subtyping rule,
it may be helpful to regard a function as a service.
The domain of the function may then be interpreted as
characterizing the restrictions imposed on the
client of the service (the caller of the function)
and the codomain of the function as somehow expressing
the benefits for the client
and the obligations for the (implementor of the) function.
Now, as we have already indicated,
to refine or improve on a service
means to relax the restrictions imposed on the client
and to strengthen the obligations of the server.
This, albeit in a syntactic way, is precisely
what is expressed by the contravariance rule for
function subtyping.