CSC447

Concepts of Programming Languages

Parametric Polymorphism

Instructor: James Riely

Monomorphic Linked Lists (C)

  • Implement linked lists in C for each pointer type?

typedef struct Node Node;
struct Node {
  int  *head;
  Node *tail;
};

int *get_last (Node *xs) {
  while (xs->tail != NULL) {
    xs = xs->tail;
  }
  return xs->head;
}
          

Generic Linked Lists (C)

  • Use (void *) type in C

typedef struct Node Node;
struct Node {
  void *head;
  Node *tail;
};

void *get_last (Node *xs) {
  while (xs->tail != NULL) {
    xs = xs->tail;
  }
  return xs->head;
}
          

Generic Linked Lists (C)

  • No static protection against casts from (void *)

typedef struct Node Node;
struct Node {
  void *head;
  Node *tail;
};
int main () {
  int  *p   = (int *)  malloc (sizeof(int));
  *p        = 2123456789;
  Node *xs  = (Node *) malloc (sizeof(Node));
  xs->tail  = NULL;
  xs->head  = p;               // store int pointer
  double *q = get_last(xs);    // alias of p
  printf ("q=%f\n", *q);       // unsafe access
}
          

$ clang -m32 parametric-03.c && ./a.out
q=96621069057346178268049192388430659584.000000
          

Monomorphic Lists (Java)

  • Implement linked lists in Java for each reference type?

static class Node {
  Integer head;
  Node    tail;
}

static Integer getLast (Node xs) {
  while (xs.tail != null) {
    xs = xs.tail;
  }
  return xs.head;
}
          

Generic Linked Lists (Java)

  • Generic list using subtype polymorphism

static class Node {
  Object  head;
  Node    tail;
}

static Object getLast (Node xs) {
  while (xs.tail != null) {
    xs = xs.tail;
  }
  return xs.head;
}
          

Subtype polymorphism

  • ClassCastException better than unsafe access

static class Node    {
  Object  head;
  Node    tail;
}
public static void main (String[] args) {
  Integer p = Integer.valueOf(2123456789);
  Node xs   = new Node();
  xs.tail   = null;
  xs.head   = p;                      // store Integer
  Double q  = (Double) getLast(xs);   // ClassCastException
  System.out.printf ("d=%f\n", q);    // unsafe access
}
          

$ javac Parametric2.java
$ java Parametric2
java.lang.ClassCastException: Integer cannot be cast to Double
	at Parametric.main(Parametric2.java:10)
          

Generic Linked Lists (Java)

  • Generic list using parametric polymorphism

static Node<X> {
  X       head;
  Node<X> tail;
}

static <X> X getLast (Node<X> xs) {
  while (xs.tail != null) {
    xs = xs.tail;
  }
  return xs.head;
}
          

Parametric Polymorphism

  • Compiler errors better than runtime exceptions

static class Node<X> {
  X       head;
  Node<X> tail;
}
public static void main (String[] args) {
  Integer p = Integer.valueOf(2123456789);
  Node<Integer> xs = new Node<>();
  xs.tail   = null;
  xs.head   = p;                      // store Integer
  Double q  = (Double) getLast(xs);   // compiler error
  System.out.printf ("d=%f\n", q);    // unsafe access
}
          

$ javac Parametric1.java
error: incompatible types: Integer cannot be converted to Double
  Double q = (Double) getLast(xs);   // compiler error
                             ^
          

Java type parameter erasure

  • Java type parameters not stored at runtime

static class ArrayList<X> {
  X[] a;

  ArrayList(int n) {
    a = new X[n];
  }
  void put (int i, X item) { a[i] = item; }
  X    get (int i)         { return a[i]; }
}
          

$ javac Parametric3.java
error: generic array creation
  a = new X[n];
          ^
          

Java type parameter erasure

  • Cast is not checked at runtime

static class ArrayList<X> {
  X[] a;

  ArrayList(int n) {
    a = (X[]) new Object[n];
  }
  void put (int i, X item) { a[i] = item; }
  X    get (int i)         { return a[i]; }
}
          

$ javac -Xlint:unchecked Parametric4.java
warning: [unchecked] unchecked cast
    a = (X[]) new Object[n];
              ^
          

Java type parameter erasure

  • Okay to ignore, since array is empty

static class ArrayList<X> {
  X[] a;
  @SuppressWarnings("unchecked")
  ArrayList(int n) {
    a = (X[]) new Object[n];
  }
  void put (int i, X item) { a[i] = item; }
  X    get (int i)         { return a[i]; }
}
          

$ javac -Xlint:unchecked Parametric4.java


// works fine
          

Java type parameter erasure

  • Mix with old code:

ArrayList<String> ss = new ArrayList<>(10);
ArrayList os = ss;
os.put (1, 2123456789);
String s = ss.get (1);
          

$ javac Parametric6.java
Note: Parametric6.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
          

$ java Parametric6                     
ClassCastException: Integer cannot be cast to class String
  at Parametric.main(Parametric6.java:4)
          

Arrays checked when assigned

  • Java stores types with arrays

String[] ss = new String[10];
Object[] os = ss;
os[1] = 2123456789;
String s = ss[1];
          

$ javac Parametric7.java

// no warnings
          

$ java Parametric7                     
ArrayStoreException: Integer
   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
    • Leading to Agda, Coq, etc
  • C++ templates are different
    • Template itself has no executable form:
      List<T>
    • Executable generated for each instantiation:
      List<int>, List<string>