2

对于 DSL,我想介绍一个dup基本调用的扩展方法Vector.fill,例如

import scala.collection.immutable.{IndexedSeq => Vec}

implicit final class Dup[A](private val in: A) extends AnyVal {
  def dup(n: Int): Vec[A] = Vector.fill(n)(in)
}

3 dup 4  // Vector(3, 3, 3, 3)

现在我想让参数成为一个按名称的值,以便以下内容可以正常工作:

math.random dup 4  // wrong: four times the same value

我正在看这个问题,所以显然没有纯值类的解决方案,只有:

final class Dup[A](in: () => A) {
  def dup(n: Int): Vec[A] = Vector.fill(n)(in())
}
implicit def Dup[A](in: => A): Dup[A] = new Dup(() => in)

math.random dup 4   // ok

...取消价值类的优势,即不涉及拳击。

所以我想知道,是否可以编写一个宏来提供非实例化解决方案,其中参数是按名称命名的?

4

1 回答 1

1

为什么不?

// Doesn't matter if it's value class or not, code generated by macro
// will contain no references to it.
implicit final class Dup[A](in: A) {
  def dup(n: Int): Vec[A] = macro Macros.dupImpl[A]
}
object Dup {
  def dup[A](in: => A, n: Int) = Vector.fill(n)(in)
}

宏实现:

import scala.reflect.macros.blackbox

object Macros {
  def dupImpl[A](c: blackbox.Context)(n: c.Expr[Int]): c.Tree = {
    import c.universe._
    val q"$conv($in)" = c.prefix.tree
    q"Dup.dup($in, $n)"
  }
}

c.prefix可以假设包含一个包含in在隐式转换中的参数的树(我们可以添加一些验证代码,如果不是,则发出编译错误)。我们只需打开它并获得表示 的原始树in。然后我们直接将其传递给Dup.dup在最终生成的代码中完全丢弃隐式转换。

唯一剩下的实例化将是Function0对象的实例化,它将代替按名称参数传递,但这是不可避免的。

于 2015-09-07T17:26:13.810 回答