====== L06. Polymorphism ====== /* (A) Subtype polymorphism: A type S is related to another type T (supertype) by some notion of substitutability, meaning that functions written to operate on elements of type T(supertype) can also operate on elements of type S (subtype) Example: */ class Animal { def scream: String = "An animal screams!" } class Wolf extends Animal { override def scream: String = "A wolf screams" } new Animal().scream /* (B) Ad-hoc polymorphism Is obtained when a function works or appears to work on several different types (which may not have anything in common), and that function may behave in unrelated ways, for each type. (Strachey 1967) (C) Parametric polymorphism Functions (or datatypes) which can be defined GENERICALLY, so that they work "identically" on families of types, without depending on them. Examples: - computing the size of a list - many other operations working on lists, and NOT DEPENDING on the contained type of the list. Scala "generics" (a little different from Java Generics) We will implement a very simple generic type: */ trait FList[A] { // a polymorphic trait, it depends on some type A. A is called type variable def length: Int def head: A def tail: FList[A] def map[B](f: A => B):FList[B] //B is another type variable def foldRight[B](acc: B)(op: (A,B) => B): B def foldLeft[B](acc: B)(op: (B,A) => B): B // we can have implementations in traits def contains(e: A): Boolean = this.foldRight(false)(_ == e || _) def indexOf(e: A): Int def update(e: A, pos: Int): FList[A] // Insertion sort // inserting in a sorted list def insSorted(f: A => Int)(x: A): FList[A] // sorting using insertion sort def sortBy(f: A => Int): FList[A] } case class FNil[A]() extends FList[A]{ override def length: Int = 0 override def head: A = throw new Exception ("head of empty list") override def map[B](f: A => B): FList[B] = FNil() override def foldRight[B](acc: B)(op :(A,B) => B):B = acc override def foldLeft[B](acc: B)(op: (B,A) => B): B = acc } case class Cons[A](x: A, xs: FList[A]) extends FList[A]{ override def length: Int = 1 + xs.length override def head: A = x override def map[B](f: A => B): FList[B] = // awkward silence Cons(f(x),xs.map(f)) override def foldRight[B](acc: B)(op :(A,B) => B):B = op(x,xs.foldRight(acc)(op)) override def foldLeft[B](acc: B)(op: (B,A) => B): B = xs.foldLeft(op(acc,x))(op) }