Polymorphism

9


Additional keywords and phrases: exceptions, type calculi, parametric types, coercion, ad hoc polymorphism, universal types, existential types, unfolding, intersection types


slide: Polymorphism


Abstract inheritance

  • declarative relation among entities

Inheritance networks

  • isa-trees -- partial ordering
  • isa/is-not -- bipolar, is-not inference

Non-monotonic reasoning


    Nixon is-a Quaker
    Nixon is-a Republican
    Quakers are Pacifists
    Republicans are not Pacifists
  

Incremental system evolution is in practice non-monotonic!


slide: Knowledge representation and inheritance


Taxonomic structure


   \A x . Elephant(x) -> Mammal(x) 
   \A x . Elephant(x) -> color(x) = gray 
   \A x . Penguin(x) -> Bird(x) /\ \neg CanFly(x) 
  

slide: Taxonomies and predicate logic

subsections:


Types as sets of values

Ideals -- over $V$

Type system

subtypes correspond to subsets



slide: The interpretation of types as sets


Sub-range inclusion

<=


  • \ffrac{n <= n' and m' <= m}{n'..m' <= n..m}

Functions

contravariance


  • \ffrac{ %s <= %s' and %t' <= %t}{%s' -> %t' <= %s -> %t }

Records

  • \ffrac{ %s_i <= %t_i for i = 1..m (m <= n) }{ {a_1: %s_1, ... , a_n: %s_n} <= {a_1: %t_1 , ... , a_m: %t_m} }

Variants

  • \ffrac{%s_i <= %t_i for i = 1..m (m <= n) }{ [a_1:%s_1 \/ ... \/ a_m:%s_m] <= [a_1:%t_1 \/ ... \/ a_n:%t_n] }

slide: The subtype refinement relation


Examples

subtyping



slide: Examples of subtyping


Function refinement

Functions -- as a service

contravariance



slide: The function subtype relation


Objects as records

  • record = finite association of values to labels

Field selection -- basic operation

  • (a = 3, b = true ).a == 3

Typing rule

  • \ffrac{ e_1 : %t_1 and ... and e_n : %t_n }{ { a_1 = e1,..., a_n = e_n } : { a_1 : %t_1, ..., a_n : %t_n } }

slide: The object subtype relation


Subtyping -- examples


  type any = { }
  type entity = { age : int }
  type vehicle = { age : int, speed : int }
  type machine = { age : int, fuel : string }
  type car = { age : int, speed : int, fuel : string }
  

Subtyping rules

  • %t <= %t
  • \ffrac{ %s_1 <= %t_1, ..., %s_n <= %t_n }{ { a_1 : %s_1, ..., a_{n + m} : %s_{n + m} } <= { a_1 : %t_1 , ..., a_n : %t_n } }

slide: Examples of object subtyping


Subtyping in Emerald -- S conforms to T


slide: The subtype relation in Emerald


Typing -- protection against errors

Untyped -- flexibility

Exceptions to monomorphic typing:


slide: The nature of types


Flavors of polymorphism


slide: Flavors of polymorphism


Inheritance -- incremental modification

Example: R = { a1, a2 } + { a2, a3 } = { a1, a2, a3 }

Independent attributes: M disjoint from P
Overlapping attributes: M overrules P

Dynamic binding


slide: Inheritance as incremental modification

subsections:


Lambda calculus -- very informal

%l


Lambda terms -- $ %L $


slide: The lambda calculus -- terms


Laws


slide: The lambda calculus -- laws


Substitution


slide: The lambda calculus -- substitution


Examples


   (\%l x.x) 1 = x[x:=1] = 1
   (\%l x.x+1) 2 = (x+1)[x:=2] = 2 + 1
   (\%l x.x+y+1) 3 = (x+y+1)[x:=3] = 3+y+1
   (\%l y.(\%l x.x+y+1) 3) 4) = 
  		 ((\%l x.x+y+1) 3)[y:=4] = 3 + 4 + 1
  

slide: Beta conversion -- examples


Properties

Proof: take W = %l x.F ( xx ) and X = WW, then


  X = WW = ( \%l x.F ( xx ) ) W = F ( WW ) = FX
  

slide: The lambda calculus -- properties


A simple type calculus -- subtypes

%l_{ <= }


  • %t ::= %r | %t_1 -> %t_2
  • e ::= x | %l x : %t . e | e_1 e_2

Type assignment

  • \ffrac{%G |- x : %s \hspace{1cm} %G |- e : %t }{ %G |- %l x : %s . e \e %s -> %t }
  • \ffrac{%G |- e_1 : %s -> %t, e_2 : %s }{ %G |- e_1 e_2 \e %t }

Refinement

  • \ffrac{ %G |- e : %s \hspace{1cm} %G |- %s <= %t}{ %G |- e : %t }

slide: The subtype calculus


Examples

Application


slide: Subtypes -- examples



  int S(int x) { return x+1; }
  int twice(int f(int), int y) { return f(f(y)); }
  int twice_S(int y) { return twice(S,y); }
  

slide: Types in C++



  int SD(double x) { return x+1; } // twice(SD) rejected
  

slide: SD example



  class P { 
P
public: P() { _self = 0; } virtual P* self() { return _self?_self->self():this; } virtual void attach(C* p) { _self = p; } private: P* _self; }; class C : public P {
C <= P
public: C() : P(this) { } C* self() { // ANSI/ISO return _self?_self->self():this; } void attach(P* p) { // rejected p->attach(self()); } void redirect(C* c) { _self = c; } private: C* _self; };

slide: Subtyping in C++


Intersection types -- overloading

%l_{ /\ }


  • %t ::= %r | %t_1 -> %t_2 | \bigwedge [ %t_1 .. %t_n ]

Type assignment

  • \ffrac{%G |- e : %t_i (i \e 1..n)}{ %G |- e : \bigwedge [ %t_1 .. %t_n ] }

Refinement

  • \ffrac{ %G |- %s <= %t_i (i \e 1..n )}{ %G |- %s <= \bigwedge [ %t_1 .. %t_n ] }
  • \bigwedge [ %t_1 .. %t_n ] <= %t_i
  • %G |- \bigwedge [ %s -> %t_1 .. %s -> %t_n ] <= %s -> \bigwedge [ %t_1 .. %t_n ]

slide: The intersection type calculus


Examples


slide: Intersection types -- examples


Overloaded function selection rules

C++


  • [1] no or unavoidable conversions -- array->pointer, T -> const T
  • [2] integral promotion -- char->int, short->int, float->double
  • [3] standard conversions -- int->double, double->int, derived* -> base*
  • [4] user-defined conversions -- constructors and operators
  • [5] ellipsis in function declaration -- ...

Multiple arguments -- intersect rule

  • better match for at least one argument and at least as good a match for every other argument

slide: Overloading in C++



  void f(int, double);
  void f(double, int);
  
  f(1,2.0); // f(int, double);
  f(2.0,1); // f(double,int);
  f(1,1); // error: ambiguous
  

slide: example



  class P;
  class C;
  
  void f(P* p) { cout << "f(P*)"; } // (1)
  void f(C* c) { cout << "f(C*)"; } // (2)
  
  class P {
  public:
  virtual void f() { cout << "P::f"; }// (3)
  };
  
  class C : public P {
  public:
  virtual void f() { cout << "C::f"; } // (4)
  };
  

slide: Static versus dynamic selection


  P* p = new P; // static and dynamic P*
  C* c = new C; // static and dynamic C*
  P* pc = new C; // static P*, dynamic C*
  
  f(p); // f(P*)
  f(c); // f(C*)
  f(pc); // f(P*)
  
  p->f(); // P::f
  c->f(); // C::f
  pc->f(); // C::f
  

Bounded polymorphism -- abstraction

F_{ <= }


  • %t ::= Top | %a | %r | %t_1 -> %t_2 | \A %a <= %t_1. %t_2
  • e ::= x | %l x:%t.e | e_1 e_2 | %L %a <= %t.e | e [ %t ]

Type assignment

  • \ffrac{%G, %a <= %s |- e : %t }{ %G |- %L %a <= %s. e \e \A %a <= %s . %t }
  • \ffrac{%G, e : \A %a <= %s . %t \hspace{1cm} %G |- %s' <= %s }{ %G |- e [ %s' ] \e %t [ %a := %s' ] }

Refinement

  • \ffrac{ %G |- %s <= %s' \hspace{1cm} %G |- %t' <= %t}{ %G |- \A %a <= %s'.%t' <= \A %a <= %s.%t }

slide: The bounded type calculus


Examples

  • id = %L %a. %l x:%a.x id : \A %a. %a -> %a
  • twice1 = %L %a.%l f: %L %b. %b -> %b. %l x:%a. f[%a](f[%a](x))
    twice1 : \A %a. \A %b. (%b -> %b) -> %a -> %b
  • twice2 = %L %a.%l f: %a -> %a. %l x:%a. f(f(x))
    twice2 : \A %a. (%a -> %a) -> %a -> %a

Applications

  • id [ Int ] ( 3 ) = 3
  • twice1 [ Int ] ( id )( 3 ) = 3
  • twice1 [ Int ] ( S ) = illegal
  • twice2 [ Int ] ( S )( 3 ) = 5

slide: Parametrized types -- examples


Bounded quantification

  • g = %L %a <= { one : Int }. %l x: %a. (x.one)
    g : \A %a <= { one : int }. %a -> Int
  • g' = %L %b . %L %a <= { one : %b }. %l x: %a. (x.one)
    g' : \A %b . \A %a <= { one : %b }. %a -> %b
  • move = %L %a <= Point. %l p:%a. %l d : Int .(p.x := p.x + d); p
    move : \A %a <= Point. %a -> Int -> %a

Application

  • g' [ Int ][ { one:Int, two : Bool }]({ one = 3, two = true }) = 3
  • move [{ x : Int, y : Int }]( { x = 0, y = 0 } )(1) = { x = 1, y = 0 }

slide: Bounded quantification -- examples



  Point* move(Point* p, int d); // require int Point::x
  Point* move(Point* p, int d) { p.x += d; return p; }
  

slide: example move



  template< class T > // requires T::value()
  class P {
  public:
  P(T& r) : t(r) {}
  int operator==( P& p) {
  	return t.value() == p.t.value();
  	}
  private:
  T& t;
  };
  

slide: Type abstraction in C++



  template< class T >
  class A { 
A<T>
public: virtual T value() = 0; }; class Int : public A<int> { // Int <= A<int> public: Int(int n = 0) : _n(n) {} int value() { return _n; } private: int _n; };

slide: Type instantiation



  Int i1, i2;
  P<Int> p1(i1), p2(i2);
  if ( p1 == p2 ) cout << "OK" << endl; 
OK

slide: Example P


Existential types -- hiding

F_{ \E }


  • %t ::= ... | \E %a <= %t_1. %t_2
  • e ::= ... | pack[ %a = %s in %t ]. e

Type assignment

  • \ffrac{ %G |- %s' <= %s \hspace{1cm} %G |- e : %t}{ pack[ %a = %s' in %t ].e \e \E %a <= %s. %t }

Refinement

  • \ffrac{ %G |- %s <= %s' \hspace{1cm} %G |- %t' <= %t }{ %G |- \E %a <= %s' . %t' <= \E %a <= %s. %t }

slide: The existential type calculus


Structure -- indeterminacy

  • Top = \E %a.%a \zline{the biggest type}
  • AnyPair = \E %a \E %b.%a \* %b \zline{any pair}
  • $(3,4):\E %a.%a {\em -- does not provide sufficient structure!}
  • $(3,4):\E %a.%a \* %a

Information hiding

  • \E %a.%a \* ( %a->Int ) \zline{object, operation}
  • x : \E %a. %a \* ( %a->Int ) \zline{ \( \leadsto \) snd ( x )( fst( x )) }

slide: Existential types -- examples


Abstract data types -- packages

  • x:\E %a.{ val : %a, op : %a -> Int }
  • x = pack[%a = Int in { val:%a, op:%a -> Int } ]((3,S))
  • x.op(x.val) = 4

Encapsulation

pack [ representation in interface ]( contents )


  • interface -- type \E %a. { val : %a, op : %a -> Int }
  • representation -- %a = Int \zline{(hidden data type)}
  • contents -- (3,S)

slide: Packages -- examples



  class event { 
event
protected: event(event* x) : ev(x) {} public: int type() { return ev->type(); } void* rawevent() { return ev; } private: event* ev; }; class xevent : public event {
X
public: int type() { return X->type(); } private: struct XEvent* X; };

slide: Hiding in C++


Self-reference -- recursive types

F_{ %m }


  • %t ::= ... | %m %a. %t [%a]
  • e ::= ... | %l( self ).{ a1 = e_1, ..., a_n = e_n }

Type assignment

  • \ffrac{ %G |- e_i : %t_i \hspace{1cm} (i = 1..n) }{ %G |- %l( self ).{ a1 = %t_1,.., a_n = %t_n } \e %m%a.{ a1:%t_1,.., a_n : %t_n }[%a] }

Refinement

  • \ffrac{ %G, %a <= %b |- %s <= %t }{ %G |- %m %a.%s[%a] <= %m %b.%t [%b] }

slide: A calculus for recursive types


Object semantics -- fixed point $ %s = F[%s] $

  • \Y ( %l( self ).F( self ) ) : %s

Unrolling -- unraveling a type

  • %m %a.F[%a] = F[ %m %a.F[%a] ]

Example


  T = \%m \%a.{ a:int, c:\%a, b:\%a->\%a }
  T_1 = { a:int, c:T, b:T->T, d:bool }
  T_2 = \%m \%a.{ a:int, c:\%a, b:T->T, d:bool }
  T_3 = \%m \%a.{ a:int, c:\%a, b:\%a->\%a, d:bool } 
  

T_1, T_2 <= T, T_3 \not<= T \zline{(contravariance)}


slide: Recursive types -- examples


Inheritance -- C = P + M

Semantics -- $\Y(C) = \Y(%l( self ).M( self )(P( self )))$


slide: Inheritance semantics -- self-reference


Object inheritance -- dynamic binding

P = %l( self ).{ i = 5, id = self }
C = %l( self ).P( self ) \with { b = true }
\Y(P):%t where %t = %m %a.{ i:int, id:%a } and P:%t->%t

Simple typing -- \Y(C):%s = { i:int, id:%t, b:bool }
Delayed -- \Y(C):%s' = %m %a.{ i:int, id:%a, b:bool }
We have %s' <= %s \zline{(more information)}


slide: Object inheritance -- dynamic binding


Contravariance


  C = \%l( self ).P( self )  \with { b = true,
      eq = \%l(o).(o.i = self.i    and 
      o.b = self.b)
   } 
  

\Y(P) : %t where %t = %m %a.{ i:int, eq:%a -> bool }
\Y(C):%s where %s = %m %a.{ i:int, id:%a -> bool, b: bool }
However %s \not<= %t \zline{(subtyping error)}


slide: Object inheritance -- contravariance


Type dependency -- is polymorphic

F-bounded constraint %a <= F[%a]\n Object instantiation: \Y(P[%s]) for %s = %m t.F[t]\n We have P[%s]:%s -> F[%s] because F[%s] = %s


slide: Bounded type constraints


Inheritance


   P = \%L \%a <= F[\%a].\%l( self: \%a).{ ... }
   C = \%L \%a <= G[\%a].\%l( self:\%a).P[\%a]( self ) \with { ... }
  

with recursive types
F[%a] = { i:int, id:%a -> bool }
G[%a] = { i:int, id:%a -> bool, b : bool }
Valid, because G[%a] <= F[%a]
However \Y(C[%s]) \not<=_{subtype} \Y(P[%t])


slide: Inheritance and constraints


Inheritance != subtyping

Eiffel



  class C inherit P redefine eq 
  feature
    b : Boolean is true;
    eq( other : like Current ) : Boolean is
    begin
        Result := (other.i = Current.i) and
  		  (other.b = Current.b)
    end
  end C
  

slide: Inheritance and subtyping in Eiffel



  p,v:P, c:C 
  
  v:=c; 
  v.eq(p);   // error p has no b 
  

slide: Example



  class C : public P { 
C++
int b; public: C() { ... } bool eq(C& other) { return other.i == i && other.b == b; } bool eq(P& other) { return other.i == i; } };

slide: Inheritance and subtyping in C++


Abstract inheritance

1



slide: Section 9.1: Abstract inheritance


The subtype relation

2

  • types -- sets of values
  • the subtype relation -- refinement rules
  • functions -- contravariance
  • objects -- as records

slide: Section 9.2: The subtype relation


Flavors of polymorphism

3

  • typing -- protection against errors
  • flavors -- parametric, inclusion, overloading, coercion
  • inheritance -- incremental modification mechanism

slide: Section 9.3: Flavors of polymorphism


Type abstraction

4

  • subtypes -- typed lambda calculus
  • overloading -- intersection types
  • bounded polymorphism -- generics and inheritance

slide: Section 9.4: Type abstraction


Existential types

5

  • hiding -- existential types
  • packages -- abstract data types

slide: Section 9.5: Existential types


Self-reference

6

  • self-reference -- recursive types
  • object semantics -- unrolling
  • inheritance -- dynamic binding
  • subtyping -- inconsistencies

slide: Section 9.6: Self-reference


  1. How would you characterize inheritance as applied in knowledge representation? Discuss the problems that arise due to non-monotony.
  2. How would you render the meaning of an inheritance lattice? Give some examples.
  3. What is the meaning of a type? How would you characterize the relation between a type and its subtypes?
  4. Characterize the subtyping rules for ranges, functions, records and variant records. Give some examples.
  5. What is the intuition underlying the function subtyping rule?
  6. What is understood by the notion of objects as records? Explain the subtyping rule for objects.
  7. Discuss the relative merits of typed formalisms and untyped formalisms.
  8. What flavors of polymorphism can you think of? Explain how the various flavors are related to programming language constructs.
  9. Discuss how inheritance may be understood as an incremental modification mechanism.
  10. Characterize the simple type calculus %l_{<=}, that is the syntax, type assignment and refinement rules. Do the same for F_{ /\ } and F_{<=}.
  11. Type the following expressions: (a) { a = 1, f = %l x:Int. x + 1 }, (b) %l x:Int . x * x , and (c) %l x:{ b:Bool, f:{ a:Bool } } -> Int.x.f(x) .
  12. Verify whether: (a) f' : 2..5 -> Int <= f:1..4 -> Int, (b) { a:Bool, f:Bool -> Int } <= { a:Int, f: Int -> Int }, and (c) %l x: { a:Bool } -> Int <= %l x: { a:Bool, f:Bool -> Int } -> Int.
  13. Explain how you may model abstract data types as existential types.
  14. What realizations of the type \E %a. { a:%a, f:%a -> Bool } can you think of? Give at least two examples.
  15. Prove that %m %a . { c:%a, b:%a -> %a } \not<= %m %a . { b : %a -> %a } .
  16. Prove that %m %a . { c : %a, b: %t -> %a } <= %t , for %t = %m %a.{ b: %a -> %a }.

slide: Questions

As further reading I recommend  [CW85] and  [Pierce93]. As another source of material and exercises consult  [Palsberg94].  [BG93] contains a number of relevant papers. An exhaustive overview of the semantics of object systems, in both first order and second order calculi, is further given in  [ObjectCalculus].