Reputation: 497
I would like to use macro to generate a setter for case classes. e.g:
case class Person(name: String, age: Int)
Macro.mkSetter[Person, String]("name") : Person => String => Person
I tried the following implementation but I keep getting the following
error: scala: Error: Unknown source file: embeddedFile--QuasiquoteCompat.scala@6....
(I am using scala 2.10.3 with macro-paradise 2.0.0-SNAPSHOT)
object Macro {
def mkSetter[A, B](fieldName: String): (A,B) => A = macro mkSetter_impl[A,B]
def mkSetter_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c : Context)(fieldName: c.Expr[String]): c.Expr[(A,B) => A] = {
import c.universe._
val (aTpe, bTpe) = (weakTypeOf[A], weakTypeOf[B])
val constructor = aTpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.getOrElse(c.abort(c.enclosingPosition, s"Cannot find constructor in ${weakTypeOf[A]}"))
val field = constructor.paramss.head.find(
_.name.decoded == fieldName.toString()
).getOrElse(c.abort(c.enclosingPosition, s"Cannot find constructor field named in $fieldName"))
c.Expr[(A,B) => A](q"{(a: $aTpe, b: $bTpe) => a.copy(${field.name} = b)}")
}
}
I do realise that _.name.decoded == fieldName.toString()
is not correct way to check method name (even if _.name.decoded == "name"
seems to be ok)
Bonus point: generalise macro with varags parameters for parameters with same type, e.g.
def mkSetter[A, B](fieldNames: String*): A => B => B ... => A = macro mkSetter_impl[A,B]
Thank you!
Upvotes: 0
Views: 350
Reputation: 13048
Seems to be caused by https://github.com/scalamacros/paradise/issues/11. This week I planned to fix that issue, so it should be fine soon. You could subscribe to updates at scalamacros.org (http://scalamacros.org/news/rss.xml) or follow me on Twitter to get notified when the fix is deployed.
Upvotes: 1