2015-09-07 76 views
2

对于DSL,我想介绍一个基本上调用Vector.fill的扩展方法,例如dup允许使用名称参数的隐式类的宏变体

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) 

现在,我想使这一论证通过-name的值,这样下会正常工作:

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

我在看this question,所以显然有一个与普通无解价值类,只有:

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 

...取消价值类的好处,没有涉及拳击。

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

回答

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对象的实例化,该对象将通过名称参数传递,但这是不可避免的。

+0

谢谢,这个工程。我从“类宏”编辑为“对象宏”,所以这反映了Scala 2.11中的当前宏API。 –

+0

'class'有什么问题?这只是一个[宏包](http://docs.scala-lang.org/overviews/macros/bundles.html) – ghik

+0

谢谢,我不知道宏包;但由于某种原因,它并没有用这种新风格编辑,所以我转向了古典风格。 –