Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Runtime resolution of type arguments using scala 2.10 reflection

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?

like image 425
Sagie Davidovich Avatar asked Sep 17 '12 11:09

Sagie Davidovich


1 Answers

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.

like image 52
Michael Lynch Avatar answered Nov 16 '22 11:11

Michael Lynch