In Scala, how can I programmatically determine the name of the fields of a case class?
case-class, reflection, scala
Solution
It's me again (two years later). Here's a different, different solution using Scala reflection. It is inspired by a blog post, which was itself inspired by a Stack Overflow exchange. The solution below is specialized to the original poster's question above.
In one compilation unit (a REPL `:paste` or a compiled JAR), include `scala-reflect` as a dependency and compile the following (tested in Scala 2.11, might work in Scala 2.10):
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object CaseClassFieldsExtractor {
implicit def makeExtractor[T]: CaseClassFieldsExtractor[T] =
macro makeExtractorImpl[T]
def makeExtractorImpl[T: c.WeakTypeTag](c: Context):
c.Expr[CaseClassFieldsExtractor[T]] = {
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.decls.collectFirst {
case m: MethodSymbol if (m.isPrimaryConstructor) => m
}.get.paramLists.head
val extractParams = fields.map { field =>
val name = field.asTerm.name
val fieldName = name.decodedName.toString
val NullaryMethodType(fieldType) = tpe.decl(name).typeSignature
q"$fieldName -> ${fieldType.toString}"
}
c.Expr[CaseClassFieldsExtractor[T]](q"""
new CaseClassFieldsExtractor[$tpe] {
def get = Map(..$extractParams)
}
""")
}
}
trait CaseClassFieldsExtractor[T] {
def get: Map[String, String]
}
def caseClassFields[T : CaseClassFieldsExtractor] =
implicitly[CaseClassFieldsExtractor[T]].get
And in another compilation unit (the next line in the REPL or code compiled with the previous as a dependency), use it like this:
scala> case class Something(x: Int, y: Double, z: String)
defined class Something
scala> caseClassFields[Something]
res0: Map[String,String] = Map(x -> Int, y -> Double, z -> String)
It seems like overkill, but I haven't been able to get it any shorter. Here's what it does:
- The `caseClassFields` function creates an intermediate `CaseClassFieldsExtractor` that implicitly comes into existence, reports its findings, and disappears.
- The `CaseClassFieldsExtractor` is a trait with a companion object that defines an anonymous concrete subclass of this trait, using a macro. It is the macro that can inspect your case class's fields because it has rich, compiler-level information about the case class.
- The `CaseClassFieldsExtractor` and its companion object must be declared in a previous compilation unit to the one that examines your case class so that the macro exists at the time you want to use it.
- Your case class's type data is passed in through the `WeakTypeTag`. This evaluates to a Scala structure with lots of pattern matching and no documentation that I could find.
- We again assume that there's only one ("primary"?) constructor, but I think all classes defined in Scala can have only one constructor. Since this technique examines the fields of the constructor, not all JVM fields in the class, so it's not susceptible to the lack of generality that marred my previous solution.
- It uses quasiquotes to build up an anonymous, concrete subclass of the `CaseClassFieldsExtractor`.
- All that "implicit" business allows the macro to be defined and wrapped up in a function call (`caseClassFields`) without being called too early, when it's not yet defined.
Any comments that could refine this solution or explain how exactly the "implicits" do what they do (or if they can be removed) are welcome.
Problem
In Scala, suppose I have a case class like this: ``` case class Sample(myInt: Int, myString: String) ``` Is there a way for me to obtain a `Seq[(String, Class[_])]`, or better yet, `Seq[(String, Manifest)]`, describing the case class's parameters?