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.
  
slide: Examples of subtyping

As a first example, when we define a function    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.