messi
messi

Reputation: 41

Scala reflection with TypeTags and singleton types

I've just started experimenting with the reflection API introduced in Scala 2.10 and would expect the code below to evaluate to true five times (REPL).

Unfortunately, only the first and last expressions really do. Can somebody explain why this is the case? It seems from the compiler's perspective all these type comparisons are ok or am I getting this wrong?

Is there a way to make this (at least one .type comparison) work?

import scala.reflect.runtime.universe._

class Test[A:TypeTag](val a:A) {
  val value:this.type=this
  def t1:TypeTag[Test[A]]=typeTag[Test[A]]
  def t2:TypeTag[this.type]=typeTag[this.type]
  def t3:TypeTag[_<:Test[A]]=typeTag[this.type]
}

val a:Test[String]=new Test("a")

a.t1.tpe<:<typeOf[Test[String]] //works as expected
a.t2.tpe<:<typeOf[Test[String]] //FAILS
a.t3.tpe<:<typeOf[Test[String]] //FAILS
a.t2.tpe<:<typeOf[a.type] //FAILS
typeOf[a.type]<:<typeOf[a.type] //this works again

Tested with Scala REPL 2.10.3 and 2.11.0-M7.

Regards,

Messi

Upvotes: 1

Views: 468

Answers (2)

som-snytt
som-snytt

Reputation: 39577

Your class doesn't know its type parameter, apparently. It may be a bug that your t2 method isn't working.

The spec says

A singleton type p .type conforms to the type of the path p.

That's kind of still true here, in so far as Test is just a Test[A]. But since the tag of A is available, you'd think it would use it.

scala> typeTag[a.type].tpe
res8: reflect.runtime.universe.Type = a.type

scala> typeTag[a.type].tpe.widen
res9: reflect.runtime.universe.Type = Test[String]

scala> typeTag[a.type].tpe.widen <:< typeOf[Test[String]]
res10: Boolean = true

scala> typeTag[a.type].tpe <:< typeOf[Test[String]]
res11: Boolean = true

scala> a.t2.tpe
res12: reflect.runtime.universe.Type = Test.this.type

scala> a.t2.tpe.widen
res13: reflect.runtime.universe.Type = Test[A]

Upvotes: 2

ghik
ghik

Reputation: 10764

The essential fact to remember here is that singleton types are not associated with objects (concrete instances). Rather, they're associated with identifiers.

This means that a single object can have multiple singleton types depending on what identifier it is assigned to. For example:

class C {
  val self = this
  val thistpe = typeOf[this.type]
  type T1 = this.type
  type T2 = self.type
}

val c1 = new C
val c2 = c1
val c3: c1.type = c1

val tpe1 = typeOf[c1.type]
val tpe2 = typeOf[c2.type]
val tpe3 = typeOf[c3.type]
val tpe4 = typeOf[c1.T1]
val tpe5 = typeOf[c1.T2]
val tpe6 = c1.thistpe

In the snippet above, tpe1, tpe3 and tpe4 will will be recognized as the same types, but the others won't.

In your case, you could try something like this instead:

class C {
  def thisTpe(implicit tag: TypeTag[this.type]) = tag.tpe
}

val c = new C
c.thisTpe =:= typeOf[c.type] // yields true

Upvotes: 1

Related Questions