Макро-вариант неявного класса, который допускает аргумент по имени

Для 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

... отменив преимущество ценностного класса в том, что в нем не участвует бокс.

Итак, мне было интересно, можно ли написать макрос, который обеспечивает решение без инстанцирования, где аргумент является поименным?

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 объект, который будет передан вместо параметра по имени, но это неизбежно.

Другие вопросы по тегам