tuxdna
tuxdna

Reputation: 8477

Extract case class field name in Scala

I have a case class:

case class A(field1: String, field2: Int)

I want to refer to the exact string "field1" in some code something like:

val q = Query("field1"-> "hello")
performQuery(q)

Now I have to be careful that I type "field1" correctly all the time. To avoid that, I want to use a reference to A.field1 as a string, something like this:

val q = Query(A.field1 -> "hello")
performQuery(q)

This will help me avoid any typing mistake, and also if I later rename those fields, I don't need to update rest of the codebase strings. I can rely on IDE for that.

How can I extract a case class field name as string?

EDIT1

Here is another variation of the usage:

// capture the field name
val FIELD1_NAME = A.field1 // this implementation can change

At this point FIELD1_NAME should contain "field1" as string

// Now use the string obtained above to create a query:
val q = Query(FIELD1_NAME -> "hello")
performQuery(q)

Upvotes: 2

Views: 1338

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170713

Found at https://gist.github.com/dwickern/b47606a1ed2319afa6e2:

import scala.annotation.tailrec
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object Macros {
  def nameOfImpl(c: blackbox.Context)(x: c.Tree): c.Tree = {
    import c.universe._

    @tailrec def extract(x: c.Tree): String = x match {
      case Ident(TermName(s)) => s
      case Select(_, TermName(s)) => s
      case Function(_, body) => extract(body)
      case Block(_, expr) => extract(expr)
      case Apply(func, _) => extract(func)
    }

    val name = extract(x)
    q"$name"
  }
  def nameOfMemberImpl(c: blackbox.Context)(f: c.Tree): c.Tree = nameOfImpl(c)(f)

  def nameOf(x: Any): String = macro nameOfImpl
  def nameOf[T](f: T => Any): String = macro nameOfMemberImpl
}

//
// Sample usage:
//

val someVariable = ""
Macros.nameOf(someVariable) // "someVariable"

def someFunction(x: Int): String = ???
Macros.nameOf(someFunction _) // "someFunction"

case class SomeClass(someParam: String)
val myClass = SomeClass("")
Macros.nameOf(myClass.someParam) // "someParam"

// without having an instance of the class:
Macros.nameOf[SomeClass](_.someParam) // "someParam"

For your use-case, Query(nameOf[A](_.field1) -> "hello").

Upvotes: 3

Related Questions