University of Calgary
mail toRob Kremer

Advanced Class-Based Features

Lecture Notes on

Abadi, M. & Cardelli, L. (1996). A Theory of Objects. New York, Springer.  Chapter 3: "Advanced Class-Based Features."

CPSC 701.01 Object Theory


Table of Contents

Separating Concepts
Classical OO languages unify three concepts: 
  • Inheritance
  • Subclassing
  • Subtyping
But these need not be unified.  This chapter looks at what happens when they are separated.
 
3.1 Object Types
Separating Specification and Implementation
Early OO languages (e.g. Simula) put specification (declaration) and implementation together. 

Later languages (C++ and more so, Java) tend to separate specification and implementation. 

  • Better for separate code development in large development teams
 
InstanceTypeOf
Revisited

A object of type InstanceTypeOf(cell) need not contain any of the implementation of class cell (if it's of a subclass type). 

How to describe this?  The only thing that is shared is the type signature of class cell. 
 

Object Protocol

A type signature is also called an object protocol 

We can take the object protocol as the type of the object 
 

example

Object types corresponding to class cell and reCell are: 
    ObjectType Cell is
      var
    contents: Integer;
      method get(): Integer;
      method
    set(n:Integer);
    end;
    ObjectType reCell is
      var
    backup:Integer;
      method get(): Integer;
      method set(n:Integer);
      method
    restore();
    end;
This lists attributes and methods and their types, but not their implementations. 
  • Suitable to describe interfaces
  • Can be implemented in more than one way.
ObjectTypeOf

Meta-notation to describe the type of class cell is ObjectTypeOf(cell) such that: 
(new c): ObjectTypeOf(c)     for any class c
 
example

We may have two different classes, c1 and c2 where 
ObjectTypeOf(c1) = ObjectTypeOf(c2)
 
 

3.2 Distinguishing Subclassing from Subtyping
Separating
Subclassing 
and Subtyping
To make object types independent of classes, we need to decide between: 
  1. subtyping based on type structure
  2. subtyping based on type names in declarations
 
Simple
Definition 
of Subtype

A (oversimplified?) definition of subtype is 
    O' <: O   if O' has the same components as O and possibly more.
(Thus, ReCell <: Cell) 

This doesn't take into account the co/contravariance of methods

We need to be especially careful with recursive types 
 

Subclasseing
implies 
Subtyping

Given simple definition above, 
 
If c' is subclass of c, then ObjectTypeOf(c') <: ObjectiveTypeOf(c)
 
  •  This is just a reformulation of the ObjectTypeOf property, and is the same as the subclassing-is-subtyping property, but with single implication instead of double implication.
 
Subsumption

This replacing subclassing-is-subtyping with subclassing-implies-subtyping
  • leads to more flexible subsumption (allowing use of interfaces)
  • maintains soundness
 
 

3.3 Type Parameters
Type Parameterization
Type parameterization allows the reuse of code for different types 
  • Not really an exclusively object-oriented technique
  • Common on modern OO languages
 
example

(assume Vegetables <: Food) 
    ObjectType Person is
      ...
      method eat(food:Food);
    end;
    ObjectType Vegetarian is
      ...
      method eat(food:Vegetables);
    end;
 
problem! 

Assuming we want Vegetarian <: Person, what's the problem with the above example? 

The method specialization of Vegetarian::eat() violates the contravariance of parameters. 

  • If a Vegetarian object is subsumed into a Person object we might legally make the Vegetarian eat meat.
 
bounded type parameterization to the rescue

    ObjectOperator PersonEating[F <: Food] is
      ...
      method eat(food:F);
    end;
    ObjectOperator VegetarianEating[F <: Vegetables] is
      ...
      method eat(food:F);
    end;
This is called bounded type parameterization
The type variable F is limited (bound) to be a subtype of Food or Vegetables respectively. 
  • VegetarianEating[Vegetables] is a type
  • VegetarianEating[Food] is not well-formed
  • Vegetarian[Vegetables] = Vegetarian
  • VegetarianEating is not a type (VegetarianEating cannot have any instances)
  • No inclusion relation between PersonEating and VegetarianEating
Now, we can at least say 
for all F <: Vegetables, VegetarianEating[F] <: PersonEating[F]

and this means "a vegetarian is a person that only eats vegetables." 
 

Another Solution: Bounded Abstract Types

    ObjectType Person is
      type F <: Food;
      ...
      var lunch:F;
      method eat(food:F);
    end;
    ObjectType Vegetarian is
      type F <: Vegetables;
      ...
      var lunch:F;
      method eat(food:F);
    end;
This is called bounded abstract type or  partially abstract types

We can build an object with the attribute lunch instantiated, then forget about the specific type F. 

Now, Vegetarian <: Person holds as long has we restrict ourselves to only feeding the people the food the they carry with them. 
 

 
 
3.4 Subclassing without Subtyping
Subclassing is not Subtyping
In section 3.2 we weakened the relationship between subclassing and subtyping 
(from subclassing-is-subtyping to subclassing-implies-subtyping

We can completely unlink the notions of subtyping and subclassing 

This is called subclassing-is-not-subtyping
 

Motivation

There is good reason to want to allow covariant arguments to methods. 
 
example

    ObjectType Max is
      var n: Integer;
      method max(other: Max): Max;
    end;
    ObjectType MinMax is
      var n: Integer;
      method max(other: MinMax): MinMax;
      method min(other: MinMax): MinMax;
    end;
Consider implementations of these types: 
    class maxClass is
      var n: Integer := 0;
      method max(other: Self): Self is
        if self
    .n > other.n then 
          return self else return
    other
        end;
      end;
    end
    ;
    class minMaxClass of maxClass is
      method min(other: Self): Self is
        if self
    .n < other.n then 
          return self else return
    other
        end;
      end;
    end
    ;
 Violation of 
Contravariance

This is all fine, but if we were to override method max() in some subtype of minMaxClass, then we would violate the contravariance of method arguments. 

Therefore: 

MinMax <: Max does not hold
 
Subclasses with contravariant occurrences of Self do not always induce subtypes. 
 
 

3.5 Object Protocols
Relationship Between Type of a Class and Type of a Subclass
Now that we have decoupled subclassing from subtyping, we need to get some relationship between a the type induced by a class and the type induced by one of its subclasses. 

Two (equivalent) alternatives: 

  • F-bounded parameterization
  • higher-order bounded parameterization
In either case, we can parameterize type definitions. 
 
example

    ObjectOperator MaxProtocol[X] is
      var n: Integer;
      method max(other: X): X;
    end;
    ObjectOperator MinMaxProtocol[X] is
      var n: Integer;
      method max(other: X): X;
      method min(other: X): X;
    end;
Eliminating
Recursion

We have eliminated the recursive type T by substituting a T-Protocol
  • T-Protocol is a function over types.
 
F-Bounded
Parameterization

MinMax <: MaxProtocol[MinMax]

Subprotocol: 

S subprotocol T if S <: T-Protocol[S]

To bound the parameterization: 

ObjectOperator P1[X <: MaxProtocol[X]] is ... end;
  • We could instantiate as P1[MinMax]
 
 Higher-Order
Bounded 
Parameterization

Take <:' to be they higher-order subtype relation (between type operators): 
P <:' P' iff P[T] >: P'[T] for all types T

Then: 

MinMaxProtocol <:' MaxProtocol

Subprotocol: 

S subprotocol T if S-Protocol <:' T-Protocol

To bound the parameterization: 

ObjectOperator P2[P <: MaxProtocol] is ... end;
  • We could instantiate as P2[MinMaxProtocol]
This is a bit better than F-bounded parameterization because: 
  • it's obvious that there is a subprotocol relation
  • it's obvious that subprotocol is a relation between operators, not types
 
Matching

Instead of working with type operators, programming languages can define a matching relation (<#): 
S<#T if S <: T-Protocol[S]   (F-bounded)
or
S<#T if S-Protocol <:' T-Protocol   (higher-order bounded)
 
Thus, we have MinMax <# Max. 
 
No Subsumption

The match relation does not enjoy a subsumption property 
 
Parameterizing

But it can be used for parameterizing over types: 
ObjectOperator P3[X <# Max] is ... end;

where the instantiation P3[MinMax] is legal. 
 

 

The University of Calgary
back to previous page
back
Up to page above
up
forward to next page  
forward

mail to Rob Kremer 
Last Modified Aug 15, 2005
CPSC 701.01 Object Theory
 Graduate Course in Software Engineering