Wednesday, October 21, 2015

Comiple time and run time type checking

object Example {
  trait Tree[+T] {
    def elem: T
    def left: Tree[T]
    def right: Tree[T]

  object EmptyTree extends Tree[Nothing] {
    def elem = throw new NoSuchElementException("EmptyTree.elem")
    def left = throw new NoSuchElementException("EmptyTree.left")
    def right = throw new NoSuchElementException("EmptyTree.right")

  class Branch[+T](val elem: T, val left: Tree[T], val right: Tree[T]) extends Tree[T] {
    override def equals(other: Any) = other match {
      case that: Branch[T] =>
        this.elem == that.elem &&
        this.left == that.left &&
        this.right == that.right
      case _ => false

object Test {
  import collection.mutable.HashSet
  import Example._
  val b1 = new Branch[List[String]](Nil, EmptyTree, EmptyTree)
  val b2 = new Branch[List[Int]](Nil, EmptyTree, EmptyTree)
  b1 == b2 // true

So the pattern matching in case that: Branch[T] … is done at run time, which leads to the question: when are types checked at compile time and how do you tell?
Another related question: what types are erased? If all types are erased, then how do you know a certain object is of type Branch[_]?