Ich werde mit einem Beispiel beginnen. Hier ist ein Äquivalent List.fill
für Tupel als Makro in 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!"
)
}
}
}
Wir können diese Methode wie folgt verwenden:
scala> TupleExample.fill(3)("hello")
res0: (String, String, String) = (hello,hello,hello)
Dieser Typ ist in vielerlei Hinsicht ein seltsamer Vogel. Erstens muss das arity
Argument eine Literal-Ganzzahl sein, da wir es zur Kompilierungszeit verwenden müssen. In früheren Versionen von Scala gab es (soweit ich weiß) keine Möglichkeit für eine Methode, auch nur festzustellen, ob eines ihrer Argumente ein Literal zur Kompilierungszeit war oder nicht.
Zweitens ist der Product
Rückgabetyp eine Lüge - der statische Rückgabetyp enthält die spezifische Arität und den Elementtyp, die durch die Argumente bestimmt werden, wie oben gezeigt.
Wie würde ich dieses Ding dokumentieren? Ich erwarte derzeit keine Scaladoc-Unterstützung, möchte aber ein Gefühl für Konventionen oder Best Practices haben (abgesehen davon, dass nur die Fehlermeldungen zur Kompilierungszeit klar sind), die das Ausführen einer Makromethode - mit ihrer potenziell bizarre Anforderungen - weniger überraschend für Benutzer einer Scala 2.10-Bibliothek.
Die ausgereiftesten Demonstrationen des neuen Makrosystems (z. B. ScalaMock , Slick , die anderen hier aufgeführten ) sind auf Methodenebene noch relativ undokumentiert. Alle Beispiele oder Hinweise wären willkommen, auch solche aus anderen Sprachen mit ähnlichen Makrosystemen.