Scala : Class 1

package fr.enst.plnc// We define an Option abstract class, covariant in its type parameter.// It means that if B derives from A, we will be able to assign an// Option[B] to an Option[A].// We add three methods. The third one is just because we love Haskell.abstractclass Option[+T] {
defisDefined(): Boolean
defget(): T
// We could have used ">>=" as the method name as well.def bind[U](f: T => Option[U]): Option[U]
}
// Here we see that we can override a method by a val. Also, the "get" val// is automatically built for a "case class" for all constructor parameters.// Also, "apply" in the automatically created companion object allows// us to build instances without writing "new", as in:// val s = Some("foobar")// We also get a functional "toString", "equals", and "hashCode". Note that// "==" calls "equals" in Scala.caseclass Some[+T](get: T) extends Option[T] {
val isDefined = truedef bind[U](f: T => Option[U]) = f(get)
}
// A "case object" is a singleton of a case class, just as an object is// a singleton of its own type. Also, we learn here that Nothing inherits// from all types, which is practical. And no, we cannot create objects// of type Nothing. Not even "null".caseobject None extends Option[Nothing] {
val isDefined = falsedef get = sys.error("no get for None")
def bind[U](f: Nothing => Option[U]) = None
}
// Those test classes were made for, well, testing.class A
class B extends A
// The list class is covariant in its type parameter.abstractclass List[+T] {
def head: T
def tail: List[T]
def isEmpty: Boolean
// Note that "::" is a perfectly valid method name. Since it ends with// ":", the method is looked up into its right argument parameter.def ::[U >: T](start: U): List[U] =
Cons(start, this)
// We need to use override when we override a concrete method. Note the// use of the string interpolator.overridelazyval toString =
if (isEmpty) "()"else s"(${internalToString})"// This is ugly. It was just to illustrate recursion from within the// string interpolator.def internalToString: String =
if (isEmpty) ""elseif (tail.isEmpty) s"$head"else s"$head, ${tail.internalToString}"
}
// Yet another case object!caseobject Nil extends List[Nothing] {
def head = sys.error("no head")
def tail = sys.error("no tail")
def isEmpty = true
}
// Yet another case class!caseclass Cons[+T](head: T, tail: List[T]) extends List[T] {
def isEmpty = false
}