可以获得这种语法是可能的,但它需要一种涉及结构类型的疯狂技巧(并且需要一行额外的样板代码)。我已经写了一篇博客文章详细讨论了这个技巧,并在这里给出一个简化的版本。
首先是宏的实现set
(请注意,我使用的是 quasiquotes,现在可以在 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)
请参阅博客文章以获取功能更全面的版本、解释以及将这种糟糕的代码引入世界的道歉。