Scala : Class 1
« Paradigmes et langages non classiques 2014
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.
abstract class Option[+T] {
def isDefined(): Boolean
def get(): 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.
case class Some[+T](get: T) extends Option[T] {
val isDefined = true
def 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".
case object None extends Option[Nothing] {
val isDefined = false
def 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.
abstract class 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.
override lazy val 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) ""
else if (tail.isEmpty) s"$head"
else s"$head, ${tail.internalToString}"
}
// Yet another case object!
case object Nil extends List[Nothing] {
def head = sys.error("no head")
def tail = sys.error("no tail")
def isEmpty = true
}
// Yet another case class!
case class Cons[+T](head: T, tail: List[T]) extends List[T] {
def isEmpty = false
}