CSC 347 - Concepts of Programming Languages

Parametric Polymorphism

Instructor: Stefan Mitsch

Learning Objectives

Inheritance for container classes?

  • Understand type parameters vs. inheritance

Monomorphic Linked Lists (C)

  • Implement for each pointer type?
  1. typedef struct Node Node;
  2. struct Node {
  3. int* item;
  4. Node* next;
  5. };
  6. int* get_last (Node* xs) {
  7. while (xs->next != NULL) {
  8. xs = xs->next;
  9. }
  10. return xs->item;
  11. }
  • Can inheritance solve this?
  1. abstract class Node {
  2. Node next;
  3. Node getLast() {
  4. Node xs = this;
  5. while (xs.next != null) xs = xs.next;
  6. return ???; // no xs.item!
  7. }
  8. }
  9. class IntNode extends Node {
  10. int item;
  11. // duplicate getLast in every concrete class?
  12. }

Benefits? Downsides?

Generic Linked Lists (C)

  • Use type (void*) in C
  1. typedef struct Node Node;
  2. struct Node {
  3. void* item;
  4. Node* next;
  5. };
  6. void* get_last (Node* xs) {
  7. while (xs->next != NULL) {
  8. xs = xs->next;
  9. }
  10. return xs->item;
  11. }

Benefits? Downsides?

Generic Linked Lists (C)

  • No static protection against casts from (void *)
  1. typedef struct Node Node;
  2. struct Node {
  3. void* item;
  4. Node* next;
  5. };
  6. int main () {
  7. int* p = (int*) malloc (sizeof(int));
  8. *p = 2123456789;
  9. Node* xs = (Node*) malloc (sizeof(Node));
  10. xs->next = NULL;
  11. xs->item = p; // store int pointer
  12. double* q = get_last(xs); // alias of p
  13. printf ("q=%f\n", *q); // unsafe access
  14. }
  1. $ clang -m32 parametric-03.c && ./a.out
  2. q=96621069057346178268049192388430659584.000000

Generic Linked Lists (Java)

  • Generic list using subtype polymorphism
  1. static class Node {
  2. Object item;
  3. Node next;
  4. }
  5. static Object getLast (Node xs) {
  6. while (xs.next != null) {
  7. xs = xs.next;
  8. }
  9. return xs.item;
  10. }

Generic Linked Lists (Java)

  • Subtype polymorphism
  • ClassCastException better than unsafe access
  1. static class Node {
  2. Object item;
  3. Node next;
  4. }
  5. public static void main (String[] args) {
  6. Integer p = Integer.valueOf(2123456789);
  7. Node xs = new Node();
  8. xs.next = null;
  9. xs.item = p; // store Integer
  10. Double q = (Double) getLast(xs); // ClassCastException
  11. System.out.printf ("d=%f\n", q); // unsafe access
  12. }
  1. $ javac Parametric2.java
  2. $ java Parametric2
  3. java.lang.ClassCastException: Integer cannot be cast to Double
  4. at Parametric.main(Parametric2.java:10)

Generic Linked Lists (Java)

  • Generic list using parametric polymorphism
  1. static Node<X> {
  2. X item;
  3. Node<X> next;
  4. }
  5. static <X> X getLast (Node<X> xs) {
  6. while (xs.next != null) {
  7. xs = xs.next;
  8. }
  9. return xs.item;
  10. }

Parametric Polymorphism

  • Compiler errors better than runtime exceptions
  1. static class Node<X> {
  2. X item;
  3. Node<X> next;
  4. }
  5. public static void main (String[] args) {
  6. Integer p = Integer.valueOf(2123456789);
  7. Node<Integer> xs = new Node<>();
  8. xs.next = null;
  9. xs.item = p; // store Integer
  10. Double q = (Double) getLast(xs); // compiler error
  11. System.out.printf ("d=%f\n", q); // unsafe access
  12. }
  1. $ javac Parametric1.java
  2. error: incompatible types: Integer cannot be converted to Double
  3. Double q = (Double) getLast(xs); // compiler error
  4. ^

Java Type Parameter Erasure

  • Java type parameters are not part of runtime type information
  1. static class ArrayList<X> {
  2. X[] a;
  3. ArrayList(int n) {
  4. a = new X[n];
  5. }
  6. void put (int i, X item) { a[i] = item; }
  7. X get (int i) { return a[i]; }
  8. }
  1. $ javac Parametric3.java
  2. error: generic array creation
  3. a = new X[n];
  4. ^

Java Type Parameter Erasure

  • Cast is not checked at runtime
  1. static class ArrayList<X> {
  2. X[] a;
  3. ArrayList(int n) {
  4. a = (X[]) new Object[n];
  5. }
  6. void put (int i, X item) { a[i] = item; }
  7. X get (int i) { return a[i]; }
  8. }
  1. $ javac -Xlint:unchecked Parametric4.java
  2. warning: [unchecked] unchecked cast
  3. a = (X[]) new Object[n];
  4. ^

Why is cast not checked at runtime?

Java Type Parameter Erasure

  • Okay to ignore, since array is empty
  1. static class ArrayList<X> {
  2. X[] a;
  3. @SuppressWarnings("unchecked")
  4. ArrayList(int n) {
  5. a = (X[]) new Object[n];
  6. }
  7. void put (int i, X item) { a[i] = item; }
  8. X get (int i) { return a[i]; }
  9. }
  1. $ javac -Xlint:unchecked Parametric4.java

Java Type Parameter Erasure

  • Not possible to check casts when writing to unparameterized list
  1. ArrayList<String> ss = new ArrayList<>(10);
  2. ArrayList os = ss;
  3. os.put (1, 2123456789);
  4. String s = ss.get (1);
  1. $ javac Parametric6.java
  2. Note: Parametric6.java uses unchecked or unsafe operations.
  3. Note: Recompile with -Xlint:unchecked for details.
  1. $ java Parametric6
  2. ClassCastException: Integer cannot be cast to class String
  3. at Parametric.main(Parametric6.java:4)

Arrays Checked When Assigned

  • Java stores types with arrays
  1. String[] ss = new String[10];
  2. Object[] os = ss;
  3. os[1] = 2123456789;
  4. String s = ss[1];
  1. $ javac Parametric7.java
  2. // no warnings
  1. $ java Parametric7
  2. ArrayStoreException: Integer
  3. at Parametric.main(Parametric7.java:3)

Parametric Polymorphism

  • In Java, C#, Ada, Haskell, Scala, Rust, etc
  • First developed in ML (1970s, modern descendants: F#, oCaml, Standard ML)
  • Strong connection to mathematical logic: Agda, Coq, etc.
  • C++ templates are different
    • Template itself has no executable form: List<T>
    • Executable generated for each instantiation: List<int>, List<string>

Summary

  • Parametric polymorphism allows independence between container class and types of contained objects
  • Type safety when reading from and writing to containers
  • Not all languages retain type parameters at runtime (Java: compiler knows generics, JVM does not)