====== Lecture 08: Polymorphism ====== /* Tipuri de polimorfism */ // Overloading (in limbaje OO si Imperative in general) // supraincarcare // (1) Mai general, polimorfism ad-hoc def toString(x: Int): String = ??? // impl 1 def toString(c: Char): String = ??? // impl 2 // Avem "un nume", "mai multe tipuri", "mai multe implementari" class Animal { def sing: String = "La la la" } class Cat extends Animal{ override def sing: String = "Miau" } def toString(a: Animal): String = ??? // impl 3 def toString(c: Cat): String = ??? // impl 4 val x: Animal = new Cat toString(x) // compile-time. Impl 3 este cea chemata // Overriding (depinde de existenta unei relatii de mostenire intre clase) val l: List[Animal] = List (new Cat, new Animal) val songs = for (a <- l) yield a.sing // overriding - la runtime // (2) Subtype polymorphism // (3) Parametric polymorphism // in Java = Genericitate (based on type-errasure). /* List l = ... for (Object x: l) ((Animal)x).sing */ // a parametrically-polymorphic function def size[A] (l:List[A]): Int = ??? // Avem "un nume", "mai multe tipuri", "o singura implementare" def foldRight[A,B](l: List[A])(acc: B)(op:(A,B) => B): B = ??? /* Mai multe despre polimorfismul parametric * * Aplicatii: Colectii (Liste, stive, map-uri, perechi, arbori) * * Sa presupunem ca vrem sa introducem error-handling in programele noastre. * * */ /* // more coding work V case object Error extends Nat{ def +(other: Nat): Nat = ??? def -(other: Nat): Nat = ??? } */ trait Nat { def +(other: Nat): Nat def +(other: Result[Nat]): Result[Nat] def -(other: Nat): Result[Nat] } trait Result[A] { def map[B](f: A => B): Result[B] } case class Value[A](v: A) extends Result[A] { override def map[B](f: A => B): Result[B] = Value(f(v)) } //case object Error extends Result[A] // covarianta tipurilor (type variance) case class Error[A](m: String) extends Result[A] { override def map[B](f: A => B): Result[B] = Error(m) } case object Zero extends Nat { override def +(other: Nat):Nat = other override def +(other: Result[Nat]): Result[Nat] = other override def -(other: Nat):Result[Nat] = other match { case Zero => Value(Zero) case _ => Error("Negative value") } } case class Succ(n: Nat) extends Nat { override def +(other: Nat): Nat = Succ(n + other) override def +(other: Result[Nat]): Result[Nat] = other match { case Error(msg) => other case Value(m) => Value(Succ(n + m)) } override def -(other: Nat): Result[Nat] = other match { case Zero => Value(this) case Succ(y) => n - y } } def fromInt(i: Int): Result[Nat] = { if (i < 0) Error("Negative integer") else if (i == 0) Value(Zero) else fromInt(i-1) match { case Error(m) => Error(m) case Value(n) => Value(Succ(n)) } } // Nat Result[Nat] Succ(Zero) + fromInt(1) //Result[Nat] Nat // asta nu va merge: fromInt(1) + Succ(Zero) fromInt(1) // Result[Nat] .map(_ + Succ(Zero)) .map(_ + fromInt(1)) // .map(_ - Succ(Zero)) trebuie sa implementam si -(other: Result[Nat]): Result[Nat] /* Despre Result!!! F.F. important Result exista deja, se numeste "Option". - None (echivalentul lui Error) - Some (echivalentul lui value) Un stil de scriere a programelor folosind Option */ def f[A](l: List[A]): Int = ??? /* La andThen - prima functie sa aiba signatura scrisa deja (cu lambda) - fiecare functie sa fie inchise in paranteze - toata constructia sa fie inchisa in paranteze */ val cutie = Some(List(1,2,3)) cutie .map(_.map(_+1)) .map(_.filter(_>0)) .map(f) .map(_+1) // comments cutie match { case Some(v) => ??? // do something } f(List(1,2,3) .map(_+1) .filter(_>0)) + 1