2

For a DSL, I want to introduce a dup extension method that basically calls Vector.fill, e.g.

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)

Now I want to make the argument a by-name value, so that the following would work correctly:

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

I am looking at this question, so apparently there is no solution with a plain value class, only:

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

...undoing the advantage of a value-class that there is no boxing involved.

So I was wondering, would it be possible to write a macro that provides a no-instantiation solution where the argument is by-name?

0__
  • 66,707
  • 21
  • 171
  • 266

1 Answers1

1

Why not?

// 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)
}

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

The c.prefix can be assumed to contain a tree for the in argument wrapped in an implicit conversion (we can add some validation code and emit compilation error if it's not). We simply unwrap it and obtain the original tree that represents in. We then pass it directly to Dup.dup completely discarding implicit conversion in the finally generated code.

The only instantition left will be instantiation of Function0 object that will be passed in place of by-name parameter, but this is unavoidable.

0__
  • 66,707
  • 21
  • 171
  • 266
ghik
  • 10,706
  • 1
  • 37
  • 50
  • Thanks, this works. I edited from `class Macros` to `object Macros`, so this reflects the current macro API in Scala 2.11. – 0__ Apr 23 '16 at 20:48
  • What was wrong with `class`? This was just a [macro bundle](http://docs.scala-lang.org/overviews/macros/bundles.html) – ghik Apr 26 '16 at 07:29
  • thanks, I didn't know about macro bundles; but it was not compiling with this new style for some reason, so I switched to the classical style. – 0__ Apr 26 '16 at 09:10