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. }

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?

  • No static protection against casts

    1. int main () {
    2. int p = (int*) malloc (sizeof(int));
    3. *p = 2123456789;
    4. Node* xs = (Node*) malloc (sizeof(Node));
    5. xs->next = NULL;
    6. xs->item = p; // store pointer
    7. double* q = get_last(xs); // alias of p
    8. printf ("q=%f\n", *q); // unsafe access
    9. }
    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. }
  • ClassCastException better than unsafe access

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

Can Inheritance Solve This?

  • Container base class
    1. abstract class Node {
    2. Object item;
    3. Node next;
    4. Object getLast() {
    5. Node xs = this;
    6. while (xs.next != null) xs = xs.next;
    7. return xs.item;
    8. }
    9. }
  • Implementations reuse getLast
    1. class IntNode extends Node {
    2. int getLastItem() {
    3. int last = (int)getLast(); // cast
    4. return last;
    5. }
    6. }

Benefits? Downsides?

  • Need type parameters
    1. class Node {
    2. ??? item;
    3. Node next;
    4. ??? getLast() {
    5. Node xs = this;
    6. while (xs.next != null) xs = xs.next;
    7. return xs.item;
    8. }
    9. }

Generic Linked Lists (Java)

  • Generic list using parametric polymorphism
  1. class Node<X> {
  2. X item;
  3. Node<X> next;
  4. X getLast () {
  5. Node<X> xs = this;
  6. while (xs.next != null) xs = xs.next;
  7. return xs.item;
  8. }
  9. }
  • Compiler errors better than runtime exceptions

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

Generic Linked Lists (Scala)

  • Generic list using parametric polymorphism
  1. class Node[X](val item: X, val next: Node[X]):
  2. def getLast () : X =
  3. var xs = this
  4. while xs.next != null do xs = xs.next
  5. xs.item

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)

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)
  • C++ templates are different
    • Template itself has no executable form: List<T>
    • Executable generated for each instantiation: List<int>, List<string>

# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Parametric Polymorphism - In Java, C#, Ada, Haskell, Scala, Rust, etc - First developed in [ML](https://www.google.com/search?q=ml+programming+language) (1970s, modern descendants: F#, oCaml, Standard ML) - Strong connection to mathematical logic: [Agda](https://wiki.portal.chalmers.se/agda), [Coq](https://coq.inria.fr/about-coq), etc. ---