parametric polymorphism antonio cisternino giuseppe attardi università di pisa

45
Parametric Parametric Polymorphism Polymorphism Antonio Cisternino Antonio Cisternino Giuseppe Attardi Giuseppe Attardi Università di Pisa Università di Pisa

Upload: joshua-campbell

Post on 18-Jan-2018

225 views

Category:

Documents


0 download

DESCRIPTION

C++ templates and macros Macros are dealt by the preprocessor Macros are dealt by the preprocessor C++ templates are implemented on the syntax tree C++ templates are implemented on the syntax tree The instantiation strategy is lazy The instantiation strategy is lazy The following class compiles unless the method foo is used: The following class compiles unless the method foo is used: template class Foo { T x; int foo() { return x + 2; } }; Foo f; f.x = “”; f.foo();

TRANSCRIPT

Page 1: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Parametric PolymorphismParametric Polymorphism

Antonio CisterninoAntonio CisterninoGiuseppe AttardiGiuseppe AttardiUniversità di PisaUniversità di Pisa

Page 2: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Parametric PolymorphismParametric Polymorphism

C++ templates implement a form of C++ templates implement a form of parametric polymorphismparametric polymorphism

PP is implemented in many flavors and many PP is implemented in many flavors and many languages: Eiffel, Mercury, Haskell, ADA, ML, languages: Eiffel, Mercury, Haskell, ADA, ML, C++, …C++, …

Improve the expressivity of a languageImprove the expressivity of a language May improve the performance of programsMay improve the performance of programs It is a form of It is a form of UniversalUniversal polymorphism polymorphism

Page 3: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

C++ templates and C++ templates and macrosmacros Macros are dealt by the preprocessorMacros are dealt by the preprocessor C++ templates are implemented on the syntax treeC++ templates are implemented on the syntax tree The instantiation strategy is lazyThe instantiation strategy is lazy The following class compiles unless the method The following class compiles unless the method foofoo

is used:is used:template <class T>class Foo { T x; int foo() { return x + 2; }};

Foo<char*> f;Foo<char*> f;f.x = “”;f.x = “”;f.foo();f.foo();

Page 4: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

A more semantic approachA more semantic approach

Parametric polymorphism has been Parametric polymorphism has been introduced also in Java and C#introduced also in Java and C# Java Generics and Generic C# for .NET

In both cases the compiler is able to check In both cases the compiler is able to check parametric classes just looking at their parametric classes just looking at their definitiondefinition

Parametric types are more than macros on Parametric types are more than macros on ASTAST

Syntax for generics is similar in both JG and Syntax for generics is similar in both JG and C#C#

Page 5: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Generics in a NutshellGenerics in a Nutshell Type parameterization for classes, interfaces, and Type parameterization for classes, interfaces, and

methods e.g.methods e.g.class Set<T> { ... } // parameterized classclass Dict<K,D> { ... } // two-parameter classinterface IComparable<T> { ... } // parameterized interfacestruct Pair<A,B> { ... } // parameterized struct (“value class”) T[] Slice<T>(T[] arr, int start, int count) // generic method

Very few restrictions on usage:Very few restrictions on usage:• Type instantiations can be primitive (only C#) or class e.g.

Set<int> Dict<string,List<float>> Pair<DateTime, MyClass>• Generic methods of all kinds (static, instance, virtual)• Inheritance through instantiated types e.g.

class Set<T> : IEnumerable<T>class FastIntSet : Set<int> Virtual methods

only in GC#!

In GJ is<T> T[] Slice(…)

Page 6: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

C#

JG

C++

More on generic methodsMore on generic methods

Generic methods are similar to template methods in C++Generic methods are similar to template methods in C++ As in C++ JG tries to infer the type parameters from the As in C++ JG tries to infer the type parameters from the

method invocationmethod invocation C# requires specifying the type argumentsC# requires specifying the type arguments Example:Example:

template <class T> T sqr(T x) { return x*x; }std::cout << sqr(2.0) << std::endl;class F { <T> static void sort(T[] a) {…} }String[] s; F.sort(s);class F { static void sort<T>(T[] a) {…} }string[] s; F.sort<string>(s);

Page 7: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Generic StackGeneric Stackclass Stack<T> {class Stack<T> { private T[] items;private T[] items; private int nitems;private int nitems; Stack<T>() { nitems = 0; items = new T[] (50); }Stack<T>() { nitems = 0; items = new T[] (50); } T Pop() { T Pop() { if (nitems == 0) throw Empty();if (nitems == 0) throw Empty(); return items[--nitems]; return items[--nitems]; }} bool IsEmpty() { return (nitems == 0); }bool IsEmpty() { return (nitems == 0); } void Push(T item){void Push(T item){ if (items.Length == nitems) {if (items.Length == nitems) { T[] temp = items; T[] temp = items; items = new T[nitems*2]; items = new T[nitems*2]; Array.Copy(temp, items, nitems); }Array.Copy(temp, items, nitems); }

items[nitems++] = item;items[nitems++] = item;}}

}}

How does the compiler

check the definition?

Page 8: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

TipTip C++ requires a space in nested parameter types: vector<vector<intC++ requires a space in nested parameter types: vector<vector<int> >> > to avoid to avoid

ambiguity with operator >>ambiguity with operator >> GJ (and C#) fixed the problem with the following grammar:GJ (and C#) fixed the problem with the following grammar:

ReferenceType ::= ClassOrInterfaceType | ArrayType | TypeVariableReferenceType ::= ClassOrInterfaceType | ArrayType | TypeVariableClassOrInterfaceType ::= Name | Name < ReferenceTypeList1ClassOrInterfaceType ::= Name | Name < ReferenceTypeList1ReferenceTypeList1 ::= ReferenceType1 | ReferenceTypeList , ReferenceType1ReferenceTypeList1 ::= ReferenceType1 | ReferenceTypeList , ReferenceType1ReferenceType1 ::= ReferenceType > | Name < ReferenceTypeList2ReferenceType1 ::= ReferenceType > | Name < ReferenceTypeList2ReferenceTypeList2 ::= ReferenceType2 | ReferenceTypeList , ReferenceType2ReferenceTypeList2 ::= ReferenceType2 | ReferenceTypeList , ReferenceType2ReferenceType2 ::= ReferenceType >> | Name < ReferenceTypeList3ReferenceType2 ::= ReferenceType >> | Name < ReferenceTypeList3ReferenceTypeList3 ::= ReferenceType3 | ReferenceTypeList , ReferenceType3ReferenceTypeList3 ::= ReferenceType3 | ReferenceTypeList , ReferenceType3ReferenceType3 ::= ReferenceType >>>ReferenceType3 ::= ReferenceType >>>TypeParameters ::= < TypeParameterList1TypeParameters ::= < TypeParameterList1TypeParameterList1 ::= TypeParameter1 | TypeParameterList , TypeParameter1TypeParameterList1 ::= TypeParameter1 | TypeParameterList , TypeParameter1TypeParameter1 ::= TypeParameter > | TypeVariable extends ReferenceType2 | TypeParameter1 ::= TypeParameter > | TypeVariable extends ReferenceType2 |

TypeVariable implements ReferenceType2TypeVariable implements ReferenceType2

Page 9: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

The semantic problemThe semantic problem The C++ compiler cannot make assumptions The C++ compiler cannot make assumptions

about type parametersabout type parameters The only way to type-check a C++ class is to The only way to type-check a C++ class is to

wait for argument specification (instantiation): wait for argument specification (instantiation): only then it is possible to check operations only then it is possible to check operations used (i.e. used (i.e. compcomp method in sorting) method in sorting)

From the standpoint of the C++ compiler From the standpoint of the C++ compiler semantic module all types are not parametricsemantic module all types are not parametric

Page 10: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Checking class definitionChecking class definition To be able to type-check a parametric class To be able to type-check a parametric class

just looking at its definition we introduce the just looking at its definition we introduce the notion of notion of boundbound

As in method arguments have a type, type As in method arguments have a type, type arguments are bound to other typesarguments are bound to other types

The compiler will allow to use values of such The compiler will allow to use values of such types as if upcasted to the boundtypes as if upcasted to the bound

Example: Example: class Vector<T: Sortable>class Vector<T: Sortable> Elements of the vector should implement (or Elements of the vector should implement (or

inherit from) inherit from) SortableSortable

Page 11: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

ExampleExampleinterface Sortable<T> {interface Sortable<T> { int compareTo(T a);int compareTo(T a);}}class Vector<T: Sortable<T>> {class Vector<T: Sortable<T>> { T[] v;T[] v; int sz;int sz; Vector() { sz = 0; v = new T[15]; }Vector() { sz = 0; v = new T[15]; } void addElement(T e) {…}void addElement(T e) {…} void sort() {void sort() { … … if (v[i].compareTo(v[j]) > 0)if (v[i].compareTo(v[j]) > 0) … … }}}} Compiler can type-

check this because v contains values that

implement Sortable<T>

Not possible in Java, because Sortable is an

interface and type T is lost.

Page 12: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Pros and ConsPros and Cons A parameterized type is checked also if no A parameterized type is checked also if no

instantiation is presentinstantiation is present Assumptions on type parameters are always explicit Assumptions on type parameters are always explicit

(if no bound is specified (if no bound is specified ObjectObject is assumed) is assumed) Is it possible to make assumptions beyond bound? Is it possible to make assumptions beyond bound? Yes, you can always cheat by upcasting to Yes, you can always cheat by upcasting to ObjectObject

and then to whatever you want:and then to whatever you want:class Foo<T : Button> { void foo(T b) { String s = (String)(Object)b; }}

Still the assumption made by the programmer is Still the assumption made by the programmer is explicitexplicit

Page 13: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

ImplementationImplementation

Alternative implementations of parametric Alternative implementations of parametric polymorphism:polymorphism:

C++ generates Abstract Syntax Tree for C++ generates Abstract Syntax Tree for method and classesmethod and classes

GJ implements generic types at compile GJ implements generic types at compile time: the JVM is not aware of parametric time: the JVM is not aware of parametric typestypes

C# assumes that CLR is aware of parametric C# assumes that CLR is aware of parametric types: the IL has been extended with generic types: the IL has been extended with generic instructions to handle with type parametersinstructions to handle with type parameters

Page 14: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Java Generics strategyJava Generics strategy

JG is an extension of JavaJG is an extension of Java The compiler verifies that generic types are The compiler verifies that generic types are

used correctlyused correctly Type parameters are dropped and the bound Type parameters are dropped and the bound

is used instead; downcasts are inserted in is used instead; downcasts are inserted in the right placesthe right places

The output is a normal class file unaware of The output is a normal class file unaware of parametric polymorphismparametric polymorphism

Page 15: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

ExampleExampleclass Vector<T> {class Vector<T> { T[] v; int sz;T[] v; int sz; Vector() {Vector() { v = new T[15];v = new T[15]; sz = 0;sz = 0; }} <U implements Comparer<T>><U implements Comparer<T>> void sort(U c) {void sort(U c) { … … c.compare(v[i], v[j]);c.compare(v[i], v[j]); … … }}}}……Vector<Button> v;Vector<Button> v;v.addElement(new Button());v.addElement(new Button());Button b = v.elementAt(0);Button b = v.elementAt(0);

class Vector {class Vector { ObjectObject[] v; int sz;[] v; int sz; Vector() {Vector() { v = new v = new ObjectObject[15];[15]; sz = 0;sz = 0; }} void sort(Comparer c) {void sort(Comparer c) { … … c.compare(v[i], v[j]);c.compare(v[i], v[j]); … … }}}}……Vector v;Vector v;v.addElement(new Button());v.addElement(new Button());Button b = Button b =

(Button)(Button)b.elementAt(0);b.elementAt(0);

Page 16: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

WildcardWildcardclass Pair<X,Y>  {  class Pair<X,Y>  {    X first;   X first;   Y second;   Y second;

}}public String pairString(Pair<?, ?> public String pairString(Pair<?, ?> p) {p) {return p.first + “, “ + p.second;return p.first + “, “ + p.second;

}}

Page 17: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Expressivity vs. efficiencyExpressivity vs. efficiency JG doesn’t improve execution speed; though it helps JG doesn’t improve execution speed; though it helps

to express genericity better than inheritanceto express genericity better than inheritance Major limitation in JG expressivity: exact type Major limitation in JG expressivity: exact type

information is lost at runtimeinformation is lost at runtime All instantiations of a generic type collapse to the All instantiations of a generic type collapse to the

same classsame class Consequences are: no virtual generic methods and Consequences are: no virtual generic methods and

pathological situationspathological situations Benefit: Java classes could be seen as generic Benefit: Java classes could be seen as generic

types! Reuse of the large existing codebasetypes! Reuse of the large existing codebase JG isn’t the only implementation of generics for JavaJG isn’t the only implementation of generics for Java

Page 18: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Generics and JavaGenerics and JavaJG/PizzaBracha, Odersky,Stoutamire, Wadler

NextGen Cartwright, Steele

PolyJ Bank, Liskov, Myers

Agesen, Freund, Mitchell

Generic CLRKennedy,Syme

Parameterized types

+ bounds

+ bounds

+

constraints

+ bounds

+ bounds

Polymorphic methods Type checking at point of definition Non-reference instantiations Exact run-time types ? Polymorphic virtual methods Type parameter variance

System

Feature

Page 19: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Problem with JGProblem with JGStack<String> s = new Stack<String>();Stack<String> s = new Stack<String>();s.push("Hello");s.push("Hello");Stack<Object> o = s;Stack<Object> o = s;Stack<Button> b = (Stack<Button>)o;Stack<Button> b = (Stack<Button>)o;// Class cast exception // Class cast exception Button mb = b.pop(); Button mb = b.pop(); Cast authorized:

both Stack<String> and Stack<Button> map to class Stack

Page 20: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Generic C# Strategy: Generic C# Strategy: GCLRGCLR Kennedy and Syme have extended CLR to support Kennedy and Syme have extended CLR to support

parametric types (the same proposal has been made parametric types (the same proposal has been made for PolyJ by Cartwright and Steele)for PolyJ by Cartwright and Steele)

In IL placeholders are used to indicate type In IL placeholders are used to indicate type arguments (!0, !1, …)arguments (!0, !1, …)

The verifier, JIT and loader have been changedThe verifier, JIT and loader have been changed When the program needs an instantiation of a When the program needs an instantiation of a

generic type the generic type the loaderloader generates the appropriate generates the appropriate typetype

The JIT can share implementation of reference The JIT can share implementation of reference instantiations (instantiations (Stack<String>Stack<String> has essentially the has essentially the same code of same code of Stack<Object>Stack<Object>))

Page 21: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Generic C# compilerGeneric C# compiler GC# compiler implements a JG like notation GC# compiler implements a JG like notation

for parametric typesfor parametric types Bounds are the same as in JGBounds are the same as in JG NO type-inference on generic methods: the NO type-inference on generic methods: the

type must be specified in the calltype must be specified in the call The compiler relies on GCLR to generate the The compiler relies on GCLR to generate the

codecode Exact runtime types are granted by CLR so Exact runtime types are granted by CLR so

virtual generic methods are allowedvirtual generic methods are allowed All type constructors can be parameterized: All type constructors can be parameterized:

struct, classes, interfaces and delegates.struct, classes, interfaces and delegates.

Page 22: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

ExampleExampleusing System;using System;namespace n {namespace n { public class Foo<T> {public class Foo<T> { T[] v;T[] v; Foo() { v = new T[15]; }Foo() { v = new T[15]; } public static public static void Main(string[] args) {void Main(string[] args) { Foo<string> f = Foo<string> f = new Foo<string>();new Foo<string>(); f.v[0] = "Hello";f.v[0] = "Hello"; string h = f.v[0];string h = f.v[0]; Console.Write(h);Console.Write(h); }} }}}}

.field private !0[] v.field private !0[] v

.method private hidebysig .method private hidebysig specialname specialname

rtspecialnamertspecialnameinstance void .ctor() cil instance void .ctor() cil

managed {managed { .maxstack 2.maxstack 2 ldarg.0ldarg.0 call instance void call instance void

[mscorlib]System.Object::.ct[mscorlib]System.Object::.ctor()or()

ldarg.0ldarg.0 ldc.i4.s 15ldc.i4.s 15 newarr !0newarr !0 stfld !0[] class n.Foo<!stfld !0[] class n.Foo<!

0>::v0>::v retret} // end of method Foo::.ctor} // end of method Foo::.ctor

Page 23: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

PerformancePerformance

The idea of extending CLR with generic types The idea of extending CLR with generic types seems good; but how about performance?seems good; but how about performance?

Although the instantiation is performed at Although the instantiation is performed at load time the overhead is minimalload time the overhead is minimal

Moreover code sharing reduces Moreover code sharing reduces instantiations, improving execution speedinstantiations, improving execution speed

A technique based on dictionaries is A technique based on dictionaries is employed to keep track of previous employed to keep track of previous instantiated typesinstantiated types

Page 24: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Expressive power of Expressive power of GenericsGenerics System System F F is a typed is a typed -calculus with -calculus with

polymorphic typespolymorphic types While Turing-equivalence is a trivial property While Turing-equivalence is a trivial property

of programming languages, for a type-system of programming languages, for a type-system being equivalent to System being equivalent to System FF it is not it is not

Polymorphic languages such as ML and Polymorphic languages such as ML and Haskell cannot fully express System Haskell cannot fully express System F F (both (both languages have been extended to fill the gap)languages have been extended to fill the gap)

System System FF can be transposed into C# can be transposed into C# http://www.cs.kun.nl/~erikpoll/ftfjp/2002/KennedySyme.pdf

Page 25: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Liskov Substitution Liskov Substitution PrinciplePrinciple Sub-Typing/Sub-Classing defines the class Sub-Typing/Sub-Classing defines the class

relation “relation “BB is a sub-type of is a sub-type of AA”, marked ”, marked BB <: <: AA.. According to the substitution principle,According to the substitution principle,

if if BB <: <: AA, then an instance of , then an instance of BB can be can be substituted for an instance of substituted for an instance of AA..

Therefore, it is legal to assign an instance Therefore, it is legal to assign an instance bb of of BB to to aa variable of type variable of type AAA a = b

Page 26: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Generics and SubtypingGenerics and Subtyping Do the rules for sub-types and assignment Do the rules for sub-types and assignment

works for generics?works for generics?If B <: A, then G<B> <: G<A>?

List<String> ls = new List<String>();List<Object> lo = ls;// Since String <: Object, so far so good.lo.add(new Object());String s = ls.get(0); // Error!

The rule B <: A G<B> <: G<A> defies the principle of substitution!

Counter example

Page 27: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Other exampleOther exampleclass B extends A { … }class B extends A { … }

class G<E> {class G<E> { public E e;public E e;}}

G<B> gb = new G<B>();G<B> gb = new G<B>();G<A> ga = gb;G<A> ga = gb;ga.e = new A();ga.e = new A();B b = gb.e; // B b = gb.e; // Error!Error!

GivenGiven B <: A B <: A, and , and assuming assuming G<B> <: G<A>G<B> <: G<A>, , then:then:G<A> ga = gb;G<A> ga = gb;would be legal.would be legal.In Java, type is erased.In Java, type is erased.

Page 28: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Bounded WildcardBounded WildcardA wildcard does not allow doing muchA wildcard does not allow doing muchTo provide operations with wildcard types, one can To provide operations with wildcard types, one can

specify specify boundsbounds::Upper BoundUpper Bound The ancestor of unknown:The ancestor of unknown:

G<? extends X>G<? extends X> JavaJavaG<T> where T : XG<T> where T : X C#C#

Lower BoundLower Bound The descendant of unknown:The descendant of unknown:G<? super Y>G<? super Y> JavaJavaG<T> where Y : TG<T> where Y : T C#C#

Page 29: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Bounded Wildcards Bounded Wildcards Subtyping RulesSubtyping RulesFor any B such that B <: A:For any B such that B <: A: G<B> <: G<? extends A>G<B> <: G<? extends A> G<A> <: G<? super B>G<A> <: G<? super B>

Page 30: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Bounded Wildcards - Bounded Wildcards - ExampleExampleG<A> ga = new G<A>();G<A> ga = new G<A>();G<B> gb = new G<B>();G<B> gb = new G<B>();G<? extends A> gea = gb;G<? extends A> gea = gb;// Can read from// Can read fromA a = gea.e;A a = gea.e;G<? super B> gsb = ga;G<? super B> gsb = ga;// Can write to// Can write togsb.e = new B();gsb.e = new B();

G<B> <: G<? extends A>G<B> <: G<? extends A>hence legalhence legal

G<A> <: G<? super B>G<A> <: G<? super B>hence legalhence legal

Page 31: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Generics and Generics and PolymorphismPolymorphismclass Shape { void draw() {…} }class Shape { void draw() {…} }class Circle extends Shape { void draw() {…} }class Circle extends Shape { void draw() {…} }class Rectangle extends Shape { void draw() {…} }class Rectangle extends Shape { void draw() {…} }

public void drawAll(Collection<Shape> shapes) {public void drawAll(Collection<Shape> shapes) { for (Shape s: shapes)for (Shape s: shapes) s.draw();s.draw();}}

Does not work. Why?Does not work. Why? Cannot be used on Cannot be used on Collection<Circle>Collection<Circle>

Page 32: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Bounded PolymorphismBounded Polymorphism Bind the wildcard: replace the type Bind the wildcard: replace the type

Collection<Shape>Collection<Shape> with with Collection<? extends Shape>Collection<? extends Shape>::

public void drawAll(Collection<? extends Shape> shapes) {

for (Shape s: shapes) s.draw();}

Now Now drawAll()drawAll() will accept lists of any subclass will accept lists of any subclass of of ShapeShape

The The ?? Stands for an unknown subtype of Stands for an unknown subtype of ShapeShape The type The type ShapeShape is the is the upper boundupper bound of the of the

wildcardwildcard

Page 33: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Bounded WildcardBounded Wildcard There is a problem when using wildcards:There is a problem when using wildcards:

public void addCircle(Collection<? extends Shape> shapes) {

shapes.add(new Circle());}

What will happen? Why?What will happen? Why?

Page 34: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Covariance, Covariance, Contravariance, InvarianceContravariance, InvarianceGiven types A and B such that B <: A, a type Given types A and B such that B <: A, a type

constructor G is said:constructor G is said: Covariant: if G<B> <: G<A>Covariant: if G<B> <: G<A> Contravariant: if G<A> <: G<B>Contravariant: if G<A> <: G<B> Invariant: if neither covariant nor Invariant: if neither covariant nor

contravariantcontravariant

Page 35: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Liskov Signature Liskov Signature RequirementsRequirements Methods argument types must obey Methods argument types must obey

contravariancecontravariance Return types must obey covarianceReturn types must obey covariance

Page 36: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Contravariance of Contravariance of arguments typesarguments typespublic class SuperType {  public class SuperType {          public virtual string GetFormattedAge(short age) {  public virtual string GetFormattedAge(short age) {                  return age.ToString();  return age.ToString();          }  }  }  }  

public class LSPLegalSubType : SuperType {  public class LSPLegalSubType : SuperType {          public override string GetFormattedAge(int age) {public override string GetFormattedAge(int age) {

// This is legal due to the Contravariance requirement -// This is legal due to the Contravariance requirement -// you're allowed to widen the argument type// you're allowed to widen the argument type                return base.GetFormattedAge((short)age);  return base.GetFormattedAge((short)age);          }  }  }  }      public class LSPIllegalSubType : SuperType {  public class LSPIllegalSubType : SuperType {          public override string GetFormattedAge(byte age) {public override string GetFormattedAge(byte age) {

// this is illegal due to the Contravariance requirement  // this is illegal due to the Contravariance requirement                  return base.GetFormattedAge((short)age);  return base.GetFormattedAge((short)age);          }  }  }  }  

Page 37: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Covariance of return typeCovariance of return typepublic class SuperType {  public class SuperType {          public virtual int GetDaysSinceLastLogin(User user) {  public virtual int GetDaysSinceLastLogin(User user) {                  return int.MaxValue;  return int.MaxValue;          }  }  }  }      public class LSPLegalSubType : SuperType {  public class LSPLegalSubType : SuperType {          public override short GetDaysSinceLastLogin(User user) {  public override short GetDaysSinceLastLogin(User user) {                  return short.MaxValue; return short.MaxValue; // Legal because it will always fit into// Legal because it will always fit into an int an int            }  }  }  }      public class LSPIllegalSubType : SuperType {  public class LSPIllegalSubType : SuperType {          public override long GetDaysSinceLastLogin(User user)  public override long GetDaysSinceLastLogin(User user)          {  {                  return long.MaxValue;return long.MaxValue;

// Illegal because it will not surely fit into an int// Illegal because it will not surely fit into an int            }  }  }  }  

Page 38: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

To wildcard or not to To wildcard or not to wildcard?wildcard? That is the question:That is the question:

interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c);}

interface Collection<E> { public boolean containsAll(Collection<?> c); public boolean addAll(Collection<? extends E> c);}

Page 39: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Lower Bound ExampleLower Bound Exampleinterface sink<T> {interface sink<T> { flush(T t);flush(T t);}}

public <T> T public <T> T flushAll(Collection<T> flushAll(Collection<T> col, Sink<T> sink)col, Sink<T> sink)

{{ T last;T last; for (T t: col) {for (T t: col) { last = t;last = t; sink.flush(t);sink.flush(t);}} return last;return last;}}

Page 40: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Lower Bound Example (2)Lower Bound Example (2)Sink<Object> s;Sink<Object> s;Collection<String> cs;Collection<String> cs;String str = flushAll(cs, s); // String str = flushAll(cs, s); // Error!Error!

Page 41: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Lower Bound Example (3)Lower Bound Example (3)public <T> T flushAll(Collection<T> col, public <T> T flushAll(Collection<T> col,

Sink<T> sink) { … }Sink<T> sink) { … }……String str = flushAll(cs, s); // String str = flushAll(cs, s); // Error!Error!

TT is now solvable as is now solvable as ObjectObject, but it is not the , but it is not the correct type: should be correct type: should be StringString

Page 42: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Lower Bound Example (4)Lower Bound Example (4)public <T> T flushAll(Collection<T> col, Sink<? public <T> T flushAll(Collection<T> col, Sink<?

Super T> sink) { … }Super T> sink) { … }……String str = flushAll(cs, s); // String str = flushAll(cs, s); // OK!OK!

Page 43: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Combining generics and Combining generics and inheritanceinheritance The inheritance relation must be extended The inheritance relation must be extended

with a new subtyping rule:with a new subtyping rule:

Can now cast up and down to Can now cast up and down to ObjectObject safely safely Note: types must be substituted because the Note: types must be substituted because the

super-class can be parametricsuper-class can be parametric

Givenclass C<T1,...,Tn> extends B

we have C<t1,...,tn> <: B[t1/T1, ..., tn/Tn]

Page 44: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Manipulating typesManipulating types Grouping values into types has helped us Grouping values into types has helped us

to build better compilersto build better compilers Could we do the same with types?Could we do the same with types? Types can be grouped by means of Types can be grouped by means of

inheritance which represents the union of inheritance which represents the union of type setstype sets

Parametric types combined with Parametric types combined with inheritance allow expressing inheritance allow expressing function on function on typestypes::class Stack<T:Object> : Container

Function name Function arguments Result type

Page 45: Parametric Polymorphism Antonio Cisternino Giuseppe Attardi Università di Pisa

Example: generic Example: generic containerscontainersclass Row<T : Control> : Controlclass Row<T : Control> : Control{ /* row of graphic controls *> }{ /* row of graphic controls *> }class Column<T : Control> : Controlclass Column<T : Control> : Control{ /* column of graphic controls */ }{ /* column of graphic controls */ }class Table<T : Control> : Row<Column<T>>class Table<T : Control> : Row<Column<T>>{ /* Table of graphic controls */ }{ /* Table of graphic controls */ }……// It generates the keypad of a calculator// It generates the keypad of a calculatorTable<Button> t = new Table<Button>(3, 3);Table<Button> t = new Table<Button>(3, 3);for (int i = 0; i < 3; i++)for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++)for (int j = 0; j < 3; j++) t[i, j].Text = (i * 3 + j + 1).ToString();t[i, j].Text = (i * 3 + j + 1).ToString();