Why a concrete implementation of an abstract type cannot be used to infer ClassTag?

113 Views Asked by At

Considering the following code:

object DelayedClassTagInference {

  trait SS {

    type TT <: Any

    implicit val ctg: ClassTag[TT] = implicitly[ClassTag[TT]]

    val fakeCtg: ClassTag[None.type] = implicitly[ClassTag[None.type]]

  }

  class Sub1 extends SS {

    override final type TT = Int
  }

  class Sub2 extends SS {

    override final type TT = Double
  }

  class Sub3 extends SS {

    override final type TT = String
  }
}

class DelayedClassTagInference extends FunSpec {

  import DelayedClassTagInference._

  it("") {

    val sub1 = new Sub1()
    println(sub1.fakeCtg)
    println(sub1.ctg)
  }
}

When Sub1 & Sub2 are initialised, type TT is already determined, so ClassTag[Int] and ClassTag[Double] can be easily inferred by using type class rules.

Unfortunately, when I run the above code. I got the following result:

scala.None$
null

So the value of ctg is null, besides from triggering an NullPointerException, this also doesn't make sense. Is it a scala bag that should be fixed later?

1

There are 1 best solutions below

8
Dmytro Mitin On BEST ANSWER

Remove modifier implicit for val ctg and you'll see that your code doesn't compile. You shouldn't define implicit ClassTag/TypeTag/WeakTypeTag manually, they should be generated by compiler automatically when type is known.

Actually when you call implicitly[ClassTag[TT]] the implicit val ctg: ClassTag[TT] you're defining right now is used, that's why it's null at runtime.

Implicits are resolved at compile time and, when you call sub1.ctg, resolving which .ctg is called happens at runtime (that's how subtyping polymorphism works). At compile time it's not known yet that it's Sub1#ctg.


Replace

implicit val ctg: ClassTag[TT] = implicitly[ClassTag[TT]] 

with

def ctg(implicit tag: ClassTag[TT]): ClassTag[TT] = implicitly[ClassTag[TT]] 

and you'll have Int at runtime instead of null.