-
Notifications
You must be signed in to change notification settings - Fork 76
Description
#253 introduced in Annotations.labelOf[T]
an implementation of extracting the @label
value that worked for non literal values (e.g. @label(SomeClass.LABEL)
as well as literal values (e.g. @label("vert")
).
This implementation, unfortunately, is terribly slow (to the point I noticed a single invocation of the method slowed a unit test by over a second, which got me to investigating).
The labelOf
implementation uses Toolbox.eval
, which invokes the back-end of the Scala compiler.
This implementation averages about 65ms per invocation on my dev system (an i7 MBP).
An implementation that only uses the introspection APIs is 1000 times quicker (something like 60 micro seconds):
def fastLabelOf[T <: Product: WeakTypeTag]: Option[String] = {
weakTypeOf[T].typeSymbol.asClass.annotations
.find(_.tree.tpe =:= typeOf[label])
.map( _.tree.children.tail.head match { case Literal(Constant(id: String)) => id })
}
This is in essence what the Marshallable
materialization macro and the original code does.
The problem is obviously that this regresses the ability to retrieve a non literal value.
A macro based implementation is 1,000,000 times quicker:
def labelOf[T]: Option[label] = macro labelOfImpl[T]
def labelOfImpl[T: c.WeakTypeTag](c: Context): c.Expr[Option[label]] = {
import c.universe._
val tpe = weakTypeOf[T]
val label = tpe.typeSymbol.asClass.annotations
.find(_.tree.tpe =:= weakTypeOf[label])
.map { annotation => q"""Some(new label(${annotation.tree.children.tail.head}))"""}
.getOrElse(q"None")
c.Expr[Option[label]](label)
}
(This is keeping the labelOf[T]: Option[label]
contract - in actual use, we only need the String value).
Unfortunately this has another problem, in that it needs a WeakTypeTag
of a real class type, which can only be produced at the time of invocation.
e.g. Calling Annotations.labelOf[MyType]
will work, but not Annotations.labelOf[T]
as we see in GremlinScala.hasLabel[CC]
. The WeakTypeTag
that is passed through hasLabel
isn't compatible with the implicit macro context WeakTypeTag
required by the macro.
At this point I'm not sure what the best resolution is (otherwise I'd have PRed this):
- There may be some Scala macro fu I don't know of to adapt the
WeakTypeTag
so it can be invoked indirectly through methods likehasLabel
. - Alternatively the API style for case class use in
GremlinScala
andScalaGraph
could change to take an implicitLabelExtractor
implementation materialized by the macro (i.e. instead of the implicit WeakTypeTag evidence provided by the type context bound now). I have this prototyped, and it works.
I'm happy to put together a PR if there's a consensus on a solution, but I can't think of anything at the moment that doesn't break API and/or ABI.