Я начну с примера. Вот эквивалент List.fill
для кортежей в качестве макроса в Scala 2.10:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object TupleExample {
def fill[A](arity: Int)(a: A): Product = macro fill_impl[A]
def fill_impl[A](c: Context)(arity: c.Expr[Int])(a: c.Expr[A]) = {
import c.universe._
arity.tree match {
case Literal(Constant(n: Int)) if n < 23 => c.Expr(
Apply(
Select(Ident("Tuple" + n.toString), "apply"),
List.fill(n)(a.tree)
)
)
case _ => c.abort(
c.enclosingPosition,
"Desired arity must be a compile-time constant less than 23!"
)
}
}
}
Мы можем использовать этот метод следующим образом:
scala> TupleExample.fill(3)("hello")
res0: (String, String, String) = (hello,hello,hello)
Этот парень странная птица в нескольких отношениях. Во-первых, аргумент arity
должен быть целым числом, так как нам нужно использовать его во время компиляции. В предыдущих версиях Scala у метода не было возможности (насколько мне известно) даже определить, является ли один из его аргументов литералом времени компиляции или нет.
Во-вторых, возвращаемый тип Product
является ложью — статический тип возвращаемого значения будет включать конкретную арность и тип элемента, определяемые аргументами. , как показано выше.
Итак, как мне задокументировать это? На данный момент я не ожидаю поддержки Scaladoc, но я хотел бы иметь представление о соглашениях или передовых практиках (помимо того, чтобы убедиться, что сообщения об ошибках во время компиляции ясны), которые позволили бы запустить метод макроса — с его потенциально странные требования — менее удивительные для пользователей библиотеки Scala 2.10.
Наиболее зрелые демонстрации новой системы макросов (например, ScalaMock, Slick, остальные перечислены здесь) все еще относительно недокументированы на уровне метода. Будем признательны за любые примеры или указатели, в том числе из других языков с похожими системами макросов.