Update: I suspect that what I want might not be possible, and I've written up a blog post with my reasoning (and some alternatives) here. I'd be very happy to be told that I'm wrong.
Suppose I want to create a instance of a trait using a factory method with a macro implementation. This method will take as an argument a path to a resource that the macro will read and parse (at compile time) into a map from strings to strings.
That part is all fairly straightforward. Now suppose I want to associate the resulting map with the instance I'm creating so that I can use it in subsequent macro calls involving that instance.
I crucially do not want the map to be a member of the instance, or to exist in any form at runtime. I also don't want to parse the same resource more than once. Here's a sketch of the kind of thing I'm aiming for:
import scala.language.experimental.macros
import scala.reflect.macros.Context
trait Foo {
def lookup(key: String): String = macro Foo.lookup_impl
}
object Foo {
def parseResource(path: String): Map[String, String] = ???
def apply(path: String): Foo = macro apply_impl
def apply_impl(c: Context)(path: c.Expr[String]): c.Expr[Foo] = {
import c.universe._
val m = path.tree match {
case Literal(Constant(s: String)) => parseResource(s)
}
val tree = reify(new Foo {}).tree
// Somehow associate the map with this tree here.
c.Expr(tree)
}
def lookup_impl(c: Context)(key: c.Expr[String]): c.Expr[String] =
/* Somehow read off the map and look up this key. */ ???
}
This seems to be the sort of thing that attachments are designed to help with (thanks to Eugene Burmako for the pointer), and I've got an attachment-based implementation that allows me to write the following:
Foo("whatever.txt").lookup("x")
Where apply_impl
attaches the map to the tree and lookup_impl
reads that attachment off the same tree, which it sees as its prefix. Unfortunately this is more or less useless, though, since it doesn't work in the foo.lookup("x")
case, where the prefix tree is just the variable foo
.
(Note that in my real use case Foo
extends Dynamic
, and I'm trying to give a macro implementation for selectDynamic
instead of lookup
, but that shouldn't be relevant here—I'm interested in the general case.)
Is there some way that I can use attachments to get what I want? Is there some other approach that would be more appropriate?