使用宏将导入语句粘贴到thunk的前面 [英] Pasting an import statement in front of a thunk using macros

查看:54
本文介绍了使用宏将导入语句粘贴到thunk的前面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Triggered by this question, I was wondering if one could write a def-macro to achieve the result:

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.似乎粘贴"其前面的导入语句还不够.有什么线索可以实现总体思路?也就是说,以下应该是综合输出

This fails to compile because: not found: value color. It seems its not enough to "paste" the import statement in front of it. Any clues if the general idea can be realised? That is, the following should be the synthetic output

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

{
  import myCar._
  color = "blue"
}

推荐答案

可以获取此语法,但是它需要一种涉及结构类型的疯狂技巧(并且需要额外的一行样板).我写了博客文章详细讨论了该技巧,并在此处给出简化版本.

It is possible to get this syntax, but it takes a kind of crazy trick involving structural types (and requires one extra line of boilerplate). I've written a blog post discussing the trick in detail, and will give a simplified version here.

首先用于set的宏实现(请注意,我使用的是准引用,现在可以在2.10中作为插件使用):

First for the macro implementation for set (note that I'm using quasiquotes, which are now available as a plugin in 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!")
       )
     )
  }
}

然后是结构类型的东西:

And then the structural type stuff:

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 {}")
  }
}

现在,我们将它们绑定在一起并定义我们的类:

Now we tie it all together and define our class:

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 the blog post for a more fully-featured version, an explanation, and an apology for introducing this awful code into the world.

这篇关于使用宏将导入语句粘贴到thunk的前面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆