I am trying to use Scala macros to create a case class map of single-parameter copy
methods, with each method accepting a Play Json JsValue
and a case class instance, and returning an updated copy of the instance. However, I am running into problems with the macro syntax for returning a function object.
Given a case class
case class Clazz(id: Int, str: String, strOpt: Option[String])
the intention is to create a map of the class's copy methods
implicit def jsonToInt(json: JsValue) = json.as[Int]
implicit def jsonToStr(json: JsValue) = json.as[String]
implicit def jsonToStrOpt(json: JsValue) = json.asOpt[String]
Map("id" -> (json: JsValue, clazz: Clazz) = clazz.copy(id = json),
"str" -> (json: JsValue, clazz: Clazz) = clazz.copy(str = json), ...)
I have found two related questions:
Using macros to create a case class field map: Scala Macros: Making a Map out of fields of a class in Scala
Accessing the case class copy method using a macro: Howto model named parameters in method invocations with Scala macros?
...but I am stuck at how I can create a function object so that I can return a Map[String, (JsValue, T) => T]
Edit: Thanks to Eugene Burmako's suggestion to use quasiquotes - this is where I'm currently at using Scala 2.11.0-M7, basing my code on Jonathan Chow's post (I switched from using (T, JsValue) => T to (T, String) => T to simplify my REPL imports)
Edit2: Now incorporating $tpe splicing
import scala.language.experimental.macros
implicit def strToInt(str: String) = str.toInt
def copyMapImpl[T: c.WeakTypeTag](c: scala.reflect.macros.Context):
c.Expr[Map[String, (T, String) => T]] = {
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head
val methods = fields.map { field => {
val name = field.name
val decoded = name.decoded
q"{$decoded -> {(t: $tpe, str: String) => t.copy($name = str)}}"
}}
c.Expr[Map[Sring, (T, String) => T]] {
q"Map(..$methods)"
}
}
def copyMap[T]: Map[String, (T, String) => T] = macro copyMapImpl[T]
case class Clazz(i: Int, s: String)
copyMap[Clazz]