7

Given a type declaration, I am able to resolve the type argument.

scala> reflect.runtime.universe.typeOf[List[Int]] match {case x:TypeRef => x.args}
res10: List[reflect.runtime.universe.Type] = List(Int)

For a runtime value, The same method doesn't work.

scala> reflect.runtime.currentMirror.reflect(List(42)).symbol.toType match {case x:TypeRef => x.args}
res11: List[reflect.runtime.universe.Type] = List(B)

Is there a way to overcome the type erasure for reflected values?

Sagie Davidovich
  • 578
  • 4
  • 14
  • Maybe you can find a viable solution after reading [this answer](http://stackoverflow.com/a/1094214/315306) – Raffaele Sep 17 '12 at 12:29
  • @Raffaele. Thanks for the reference. Unfortunately the question (and the answer) deal with the case when the types are concrete and are known during compile time. Manifests are deprecated now, and TypeTags (which replaced manifests) require concrete tags. – Sagie Davidovich Sep 17 '12 at 12:49
  • 1
    Use AbsTypeTag (in RC1 renamed to WeakTypeTag). More info here: http://stackoverflow.com/questions/12218641/scala-2-10-what-is-a-typetag-and-how-do-i-use-it – Eugene Burmako Sep 17 '12 at 19:47

1 Answers1

6

An example based on TypeTag knowledge gained from reading Scala: What is a TypeTag and how do I use it? posted by Eugene Burmako in the comments on your question:

import scala.reflect.runtime.universe._

object ScalaApplication {
  def main(args: Array[String]) {
    printType(List(42))
    printType(List("42"))
    printType(List("42", 42))
  }    

  def printType[T : TypeTag](t: T) {
    println(typeOf[T])
  }
}

This should give the output:

$ scala ScalaApplication.scala 
List[Int]
List[String]
List[Any]

[UPDATE 1:]

However, if you want to be aware of the type assigned to a reference of type Any you might have to opt for some sort of type aware wrapper:

import scala.reflect.runtime.universe._

object ScalaApplication {
  def main(args: Array[String]) {
    val anyWrapper = new AnyWrapper

    List(1,2,3).foreach { i =>
      i match {
        case 1 => anyWrapper.any = 42
        case 2 => anyWrapper.any = "a string"
        case 3 => anyWrapper.any = true
      }
      print(anyWrapper.any)
      print(" has type ")
      println(anyWrapper.typeOfAny)
    }
  }

  class AnyWrapper {
    private var _any: Any = null
    private var _typeOfAny: Type = null

    def any = _any
    def typeOfAny = _typeOfAny
    def any_=[T: TypeTag](a: T) = {
      _typeOfAny = typeOf[T]
      _any = a
    }

  }
}

This should give the output:

$ scala ScalaApplication.scala 
42 has type Int
a string has type String
true has type Boolean

But this solution still does not cover the case where the reference type is unknown at compile time.

[UPDATE 2:]

If the types are explicitly cast to reference of type Any, you might have to enumerate all the possible types in a match statement in order to recover the type:

import scala.reflect.runtime.universe._

object ScalaApplication {
  def main(args: Array[String]) {

    List(1,2,3).foreach { i =>
      val any: Any = i match {
        case 1 => 42.asInstanceOf[Any]
        case 2 => "a string".asInstanceOf[Any]
        case 3 => true.asInstanceOf[Any]
      }
      print(any)
      print(" has type ")
      println(matchType(any))
    }
  }

  def matchType(any: Any) = {
    any match {
      case a: Int => typeOf[Int]
      case a: String => typeOf[String]
      case a: Boolean => typeOf[Boolean]
    }
  }
}

This should give the output:

$ scala ScalaApplication.scala
42 has type Int
a string has type String
true has type Boolean

But this solution requires you to know (and list) all the possible types that you could receive in the any value.

Community
  • 1
  • 1
Michael Lynch
  • 208
  • 1
  • 5
  • This solution works only when the type is known in compile time. Here's a result of printType on an object for which the value is not known in compile-time: scala> val x:Any = List(42) x: Any = List(42) scala> printType(x) Any – Sagie Davidovich Sep 19 '12 at 21:00
  • From your original question I thought that you specifically wanted to be able to get type parameters at runtime for things like `List(42)` where the type args just are not given explicitly. – Michael Lynch Sep 20 '12 at 04:53
  • I've updated my answer with an example of a wrapper that might be closer to what you need. Would such a wrapper solve your problem? – Michael Lynch Sep 20 '12 at 06:00
  • if I use your example and add .asInstanceOf[Any], the proposed method won't be able to uncover the type. In the case that I'm exploring, the type is not known during compile time. Here are the changes that I made in your proposed solution to make it "fail": case 1 => anyWrapper.any = 42.asInstanceOf[Any] case 2 => anyWrapper.any = "a string".asInstanceOf[Any] case 3 => anyWrapper.any = true.asInstanceOf[Any] – Sagie Davidovich Sep 21 '12 at 07:37
  • Ah OK, I see. I've edited the answer again with a solution that uses `asInstanceOf[Any]` as you suggest. Does this solve the problem? – Michael Lynch Sep 21 '12 at 12:06
  • Well, we're getting closer, but not quite there yet :) Consider that you don't know the list of all possible types. would it still be possible to do? – Sagie Davidovich Sep 21 '12 at 15:21
  • I seriously doubt that you'd be able to do that. If I ever come up with some sort of workaround for that I'll be sure to post it. – Michael Lynch Sep 25 '12 at 06:05
  • I see heuristic ways to guess it, by evaluating the types of the collection elements in runtime, though it has serious performance implications. – Sagie Davidovich Sep 25 '12 at 09:29