この構文を取得することは可能ですが、構造型を含む一種のクレイジーなトリックが必要です (また、ボイラープレートが 1 行余分に必要になります)。このトリックについて詳しく説明したブログ投稿を書いたので、ここで簡略化したバージョンを示します。
まず、マクロの実装についてset
(2.10 でプラグインとして利用できる準引用符を使用していることに注意してください):
import scala.reflect.macros.Context
import scala.language.experimental.macros
trait SetterBuilder {
def set_impl(c: Context)(assignments: c.Expr[Unit]): c.Expr[Unit] = {
import c.universe._
val rewriteOne: PartialFunction[Tree, Tree] = {
case q"${_}.$n($v)" => q"${c.prefix}.$n($v)"
}
val rewrite: PartialFunction[Tree, Tree] = rewriteOne orElse {
case block: Block => q"{ ..${block collect rewriteOne} }"
}
c.Expr(
rewrite.lift(assignments.tree).getOrElse(
c.abort(c.enclosingPosition, "Not a set of assignments!")
)
)
}
}
そして、構造型のもの:
trait SyntaxBuilder {
def syntax_impl[A: c.WeakTypeTag](c: Context) = {
import c.universe._
val anon = newTypeName(c.fresh())
val declarations = c.weakTypeOf[A].declarations
val (getters, setters) = declarations.collect {
case sym: MethodSymbol if sym.isSetter => (
q"def ${sym.getter.name} = ???",
q"def ${sym.name}(x: ${sym.paramss.head.head.typeSignature}) = ???"
)
}.unzip
c.Expr[Any](q"class $anon { ..$getters; ..$setters }; new $anon {}")
}
}
すべてを結び付けて、クラスを定義します。
object Evil extends SyntaxBuilder with SetterBuilder {
def syntax[A] = macro syntax_impl[A]
}
case class Car(var speed: Int, var color: String) {
def set(assignments: Unit): Unit = macro Evil.set_impl
}
object Car {
val syntax = Evil.syntax[Car]
}
ボイラープレートを邪魔にならないようにします。
import Car.syntax._
これで完了です。
scala> val car = new Car(0, "blue")
car: Car = Car(0,blue)
scala> car set {
| color = "red"
| speed = 10000
| }
scala> car
res0: Car = Car(10000,red)
より完全な機能を備えたバージョン、説明、およびこのひどいコードを世界に導入したことに対する謝罪については、ブログ投稿を参照してください。