Contents What is genericity? Alternatives: Genericity and its - - PDF document

contents
SMART_READER_LITE
LIVE PREVIEW

Contents What is genericity? Alternatives: Genericity and its - - PDF document

Contents What is genericity? Alternatives: Genericity and its Implementation multiple instantiation casting Checking correctness of generic types at Meyer Chapter 10, 16.4 definition: constrained genericity Subtyping


slide-1
SLIDE 1

1

Genericity and its Implementation

Meyer Chapter 10, 16.4

2

Contents

  • What is genericity?
  • Alternatives:

– multiple instantiation – casting

  • Checking correctness of generic types at

definition: constrained genericity

  • Subtyping relations between instances
  • Implementation of genericity
  • Anchor Types

3

What is Genericity?

  • We don’t want to write a Stack for Integers, floats,

employees, etc.

– More effort – Versions will become inconsistent, extra debugging effort

  • Write once, use for many types
  • Implementations:

– C++: templates – Eiffel: constrained genericity – Java: not present, expected in Java 1.5

  • Problem: different types allow for different operations.

4

Examples of Genericity

  • We have seen Stack[G].
  • Vectors of numbers with addition and
  • rdering
  • A map of (key, element) pairs
  • A binary search tree for types that have a ≤

function

5

Two Ideas

  • Automatic multiple instantiation (C++)
  • Automatic casting

6

The Alternative: Casting

  • How do we write a stack in Java?

– No genericity (expected in 1.5, in 2004) – Stack is a stack of Objects – Every push and pop uses a cast

slide-2
SLIDE 2

7

Reminder: cast up

  • cast: compile-time construct to change

changes the static type (compile-time type)

  • f an object
  • Cast up (widening): See an object as if it

were of supertype. Always legal.

– Figure f = (Figure) new Rectangle();

  • r

– Figure f = new Rectangle();

8

Reminder: cast down

  • Cast down (narrowing): see an object as a subtype

– No guarantee that it works

– Rectangle r = (Rectangle) f; //Java – Rectangle *r = dynamic_cast<Rectangle *> f; //C++

– Check for legality at runtime: if incorrect,

  • throw exception (Java) or
  • assign NULL (Eiffel, C++)

– Useful for such things as: “Print diameter of all rectangles in list of figures”

9

Casting and Introspection

  • Can check beforehand if a cast is legal

– In Java:

if(f instanceof Rectangle) r = (Rectangle) f; else r = null;

– In C++ (similar in Eiffel)

if(Rectangle *r = dynamic_cast<Rectangle *> f) OK else f is not a rectangle

10

Casting Instead of Genericity

Stack s; Circle c; Figure f; Rectangle r; s.push((Object) c); s.push((Object) c); f = (Figure) s.pop(); //legal narrowing r = (Rectangle) s.pop(); //illegal narrowing

  • Alternative to Genericity
  • Effectively disables static type checking in Java!
  • Errors caught during run time, not compile time.

11

Checking Correctness

  • Not all templates can be used with all types!

12

Checking Correctness: Vector

class Vector[G]{ G get(int i){...} put(int i, G item) {...} infix “+”(other: Vector[G]): Vector[G]{ Vector[G] result; G item; require length = other.length; for(i = 0; i < length; length++){ item = item(i) + other.item(i); result.put(i, item); } return result; } }

slide-3
SLIDE 3

13

Checking Correctness

  • How do we make sure a template is correct?
  • Parameter may allow only certain
  • perations: we can add numbers, not

employees

  • Vector[Employee] is nonsense.
  • How do we make sure?

14

Correctness at Instantiation

  • Instantiation: creation of code from a declaration

Stack<Integer> or similar

  • First method to check template correctness. Used in C++
  • When template is encountered

– Check syntax – No type checks

  • When template is used, check for correctness:

– instantiate template by creating code as if it were a macro – check instantiated code

  • Drawback?

15

Correctness at Instantiation

  • Drawback:

– Have to tell user the constraints for the parameter – You never know if a template is “correct” unless you have used it in all possible ways

16

Correctness at Definition

  • Second method to check template correctness: The

Eiffel Method

  • Give argument a type, allow only instantiations of

correct type: Constrained Genericity

  • Classname[G -> H] instantiates generic type

Classname, in which G must be subclass of H

  • Fits “strong typing” idea

17

Correctness at Definition

deferred class Numeric feature infix “+”, infix “-”, infix “*”, prefix “-”, zero(), one() end

  • A ring: two groups, defined by (*,1) and

(+,0), the latter of which is commutative, such that * distributes over +.

18

Correctness at Definition

class Vector[G -> Numeric]{ G get(int i){...} put(int i, G item) {...} Vector[G] plus(other: Vector[G]){ Vector[G] result; G item; require length = other.length; for(i = 0; i < length; length++){ item = item(i) + other.item(i); result.put(i, item); } return result; }

slide-4
SLIDE 4

19

Correctness at Definition

  • class Sortable[G->Comparable]...
  • class Dictionary[G->Hashable, H]
  • class Stack[G] (= Stack[G -> Object])

– Any type has ==, !=, clone, equal,...

20

Correctness

In favor of correctness at instantiation Flexibility: No need to create classes that combine all needed functionality and to derive from them In favor of correctness at definition Correctness clear when template written (good for libraries) Both have Correctness at compile time!

21

Subtyping

  • If Truck is derived from Vehicle, is

Stack[Car] derived from Stack[Vehicle]?

22

Subtyping

  • If Truck is derived from Vehicle, is Stack[Car] derived

from Stack[Vehicle]?

  • No. (Sorry.) We could otherwise perform (code in Java):

Stack[Car] cs = new Stack[Car](); Stack[Vehicle] vs = cs; vs.push(Truck t); Car c = cs.pop(); // would assign t to car

  • Sharing does not work, copying is OK:
  • We can write conversion operators that take Stack[Car]

and yield a new object Stack[Vehicle]. Invalid code!

23

Implementation

  • The compiler perspective
  • Translating Java + templates to Java. How

would you do it?

– Odersky and Wadler, Pizza into Java, translating theory into practice,

  • Proc. Symposium on Principles of Programming Languages (POPL’97),

146– 159, 1997.

  • Two alternatives

– Multiple Instantiation – Casting

24

Multiple Instantiation

  • First implementation: C++ Method
  • Like macro expansion
  • For every type X if Stack[X] occurs in code,

recompile code for Stack[G] with X in place

  • f G

– This is when error checks are done

  • In place of means simple syntactic

substitution

slide-5
SLIDE 5

25

Casting up and down

  • Second implementation
  • Classname[G -> H] is implemented by

Classname, in which G is replaced by H (If no H

present, use Object.)

  • Every transfer from and to Classname is cast
  • At compile time, we can check that

– Generic type definition is OK when instantiated with H – Class instantiations are correct (use subclass T of H)

26

Casting: Translation Into Java

Class Stack[G]{ push(G f){ ... } } Stack[Figure] s; Circle c; Figure f; Rectangle r; Object o; s.push(c); s.push(r); s.push(o); f = s.pop();

  • = s.pop();

c = s.pop();

27

Casting: Translation Into Java

Class Stack[G->Figure]{ push(G f){ ... } } Stack[Polygon] s; Triangle t; Polygon f; Rectangle r; Object o; s.push(t); s.push(r); s.push(o); f = s.pop();

  • = s.pop();

t = s.pop(); //compile code with Figure in place of G Class Stack{ push(Figure f){ ... } } Stack s; // OK: Polygon derived from Figure Triangle t; Polygon f; Rectangle r; Object o; s.push((Figure) t); s.push((Figure) r); // compile-time error! f = (Figure) s.pop();

  • = (Object) s.pop();

//compile-time error

28

Multiple Instantiation vs. Casting

Multiple instantiation:

  • Takes more space for

program text

  • No Time overhead

Casting

  • Takes less space
  • No Time overhead

Note: Casting unnecessary: we can check correctness at compile time!

29

Final Problem: Anchor Types

  • How do we complete the definition of

Numeric, so that it can be used for Vector?

  • A class C is numeric if it has a plus operator

that takes two arguments of type C and returns a result of type C

30

Anchor Types

deferred class Numeric feature infix “+”, infix “-”, infix “*”, prefix “-”, zero(),

  • ne()

end

class Vector[G -> Numeric]{ G get(int i){...} put(int i, G item) {...} Vector[G] plus(other: Vector[G]){ Vector[G] result; G item; require length = other.length; for(i = 0; i < length; length++){ item = item(i) +

  • ther.item(i);

result.put(i, item); } return result; }

slide-6
SLIDE 6

31

Anchor Types

deferred class Numeric feature infix “+”, infix “-”, infix “*”, prefix “-”, zero(), one() end First proposal: write feature infix “+”(other: Numeric): Numeric{…} Won’t work vector[Real] cannot contain arbitrary Numerics!

class Vector[G -> Numeric]{ G get(int i){...} put(int i, G item) {...} Vector[G] plus(other: Vector[G]){ Vector[G] result; G item; require length = other.length; for(i = 0; i < length; length++){ item = item(i) +

  • ther.item(i);

result.put(i, item); } return result; } 32

Anchor Types

deferred class Numeric feature infix “+”(other: like Current): like Current ... end end

  • Like current means “of same type as current instance”, e.g.

class Integer inherit Numeric ... end

has method infix “+” with type Integer X Integer → Integer.

33

Conclusions

  • Genericity useful if static typing is

important (i.e., always)

  • Implement by multiple instantiation or

casting

  • Check correctness at instantiation or at

definition (constrained genericity)

  • Anchor types help define interfaces