隐式类的宏变体,允许使用by-name参数

时间:2015-09-07 15:26:11

标签: scala implicit scala-macros callbyname

对于DSL,我想介绍一种基本上调用dup的{​​{1}}扩展方法,例如

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)

我正在查看this question,所以显然没有纯值类的解决方案,只有:

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

...撤消没有拳击的价值类的优势。

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

1 个答案:

答案 0 :(得分: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)
}

宏impl:

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对象的实例化,它将被传递来代替按名称参数,但这是不可避免的。