/*
(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)
}