可以获得这种语法,但是需要一种涉及结构类型的疯狂技巧(并且需要额外的一行样板文件)。我写过一篇博文 http://meta.plasm.us/posts/2013/08/30/horrible-code/详细讨论该技巧,并将在此处给出简化版本。
首先是宏实现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)
See 博客文章 http://meta.plasm.us/posts/2013/08/30/horrible-code/获取功能更齐全的版本、解释以及为将这段糟糕的代码引入世界而道歉。