0
import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._

def getType[T: TypeTag](obj: T) = typeOf[T]

case class Thing(
  val id: Int,
  var name: String
)
val thing = Thing(1, "Apple")

val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature

dataType match {
 case t if t =:= typeOf[Int] => println("I am Int")
 case t if t =:= typeOf[String] => println("String, Do some stuff")
 case _ => println("Absurd")
}

Not able to digest why result is Absurd instead of I am int?

My aim is to know data-type of class parameter at runtime and match it to predefined types.

3
  • 1
    What's the goal? Looks like an x/y question where you think that runtime reflection is the way to complete a goal you don't explain (while runtime reflection is rarely the only way to go, and even less the good one). Commented Nov 29, 2020 at 16:24
  • @cchantep Actually, because the question is about Types similar question can be asked about compile-time reflection. Commented Nov 29, 2020 at 17:32
  • Connected users.scala-lang.org/t/… Commented Nov 30, 2020 at 20:36

2 Answers 2

4

Both current dataType and typeOf[Int] are printed as Int but if you do showRaw you'll see why they don't match

showRaw(dataType) // NullaryMethodType(TypeRef(ThisType(scala), scala.Int, List()))
showRaw(typeOf[Int]) // TypeRef(ThisType(scala), scala.Int, List())

The thing is that just the type Int and the type of nullary method returning Int are different types.

Try to add .resultType

val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature.resultType

dataType match {
  case t if t =:= typeOf[Int] => println("I am Int")
  case t if t =:= typeOf[String] => println("String, Do some stuff")
  case _ => println("Absurd")
} // I am Int

It's also worth mentioning that .decl(TermName("id")) returns getter symbol, it's .decl(TermName("id ")) (with a blank space) that returns field symbol. So alternatively you can do with a blank space in the symbol name and without .resultType

val dataType = getType(thing).decl(TermName("id ")).asTerm.typeSignature

I'll add to @TomerShetah's answer that if the goal is "pattern matching" all fields of a case class then this can be done also at compile time (mostly) with Shapeless:

import shapeless.Poly1
import shapeless.syntax.std.product._

object printTypes extends Poly1 {
  implicit val int: Case.Aux[Int, Unit] = at(t => println(s"I am Int: $t"))
  implicit val string: Case.Aux[String, Unit] = at(t => println(s"String, Do some stuff: $t"))
  implicit def default[V]: Case.Aux[V, Unit] = at(t => println(s"Absurd: $t"))
}
  
thing.toHList.map(printTypes)
// I am Int: 1
// String, Do some stuff: Apple

https://scastie.scala-lang.org/DmytroMitin/N4Idk4KcRumQJZE2CHC0yQ

Sign up to request clarification or add additional context in comments.

Comments

1

@Dmytrio answer is a great explanation why the reflection didn't work as you expected.

I can understand from your question, that what you are trying to do, is actually pattern match all variables you have in a case class. Please consider doing it in the following way:

case class Thing(id: Int, name: String)
val thing = Thing(1, "Apple")

thing.productIterator.foreach {
  case t: Int => println(s"I am Int: $t")
  case t: String => println(s"String, Do some stuff: $t")
  case t => println(s"Absurd: $t")
} 

Code run at Scastie.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.