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