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. その前に import ステートメントを「貼り付ける」だけでは十分ではないようです。一般的なアイデアを実現できるかどうかの手がかりはありますか? つまり、以下は合成出力である必要があります

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

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

1 に答える 1

3

この構文を取得することは可能ですが、構造型を含む一種のクレイジーなトリックが必要です (また、ボイラープレートが 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)

より完全な機能を備えたバージョン、説明、およびこのひどいコードを世界に導入したことに対する謝罪については、ブログ投稿を参照してください。

于 2013-08-30T23:58:04.500 に答える