3

由这个问题触发,我想知道是否可以编写一个 def-macro 来实现结果:

import scala.reflect.macros.Context
import language.experimental.macros

object CarImpl {
  def impl(c: Context)(fun: c.Expr[Unit]): c.Expr[Unit] = {
    import c.universe._
    val all  = "_": TermName
    val imp  = c.Expr(Import(c.prefix.tree, ImportSelector(all, -1, all, -1) :: Nil))
    val tree = reify {
      imp.splice
      fun.splice
    } .tree
    c.Expr(tree)
  }
}
class Car(var speed: Int, var color: String) {
  def set(fun: Unit): Unit = macro CarImpl.impl
}

应用:

val myCar = new Car(5, "red")
myCar.set { color = "blue" }

这无法编译,因为:not found: value color。将导入语句“粘贴”在它前面似乎还不够。如果可以实现总体想法,有什么线索吗?也就是说,下面应该是合成输出

val myCar = new Car(5, "red")

{
  import myCar._
  color = "blue"
}
4

1 回答 1

3

可以获得这种语法是可能的,但它需要一种涉及结构类型的疯狂技巧(并且需要一行额外的样板代码)。我已经写了一篇博客文章详细讨论了这个技巧,并在这里给出一个简化的版本。

首先是宏的实现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)

请参阅博客文章以获取功能更全面的版本、解释以及将这种糟糕的代码引入世界的道歉。

于 2013-08-30T23:58:04.500 回答