I just started studying scala compile-time reflection, and I got introduced to quasiquotes by the Scala official guides.
One concept I'm still struggling with is how am I supposed to work with quasiquotes (or reify, for that matter) if I want to generate the AST for an already defined object. Suppose I have an Object:
object MyObject {
def method1() = "m1"
}
In order to get a tree, I know I can do
q"""{object MyObject {
def method1() = "m1"
}}
"""
Doing this, however, prevents me from having the object actually defined in my scope (and I also need to define it entirely inside a String, throwing all code safety out of the window).
What I'd like to do to get that tree is something like this:
object MyObject {
def method1() = "m1"
}
q"$MyObject" // or q"{MyObject}", I still don't fully understand the table on the Scala guide
I want to define the object, and, afterwards, use that definition to perform some checks over it (and throw some exception in compile-time, if need be), using a macro. To use a macro, I'll need to tree (or, at least, the expression), as far as I understood.
I already know how to do the checks I want using Scala reflection in run-time, but I thought using ASTs could be a good idea (and, on the process, I would learn something). I'm getting the feeling that I'm misunderstanding some basic concept on how to use ASTs, though - it seems like one can generate ASTs based on code declared on the call site only. I'm confused.
What am I misunderstanding here?
A quasiquote
or
are just ways to write a tree
The same can be obtained with
context.parse(compile-time) /toolBox.parse(runtime) from ordinaryStringThere is compile time of macros and runtime of macros. There is compile time of main code and its runtime. Runtime of macros is compile time of main code.
MyObjectinand
MyObjectinexist in different contexts. The former exists in the current context, the latter exists in the context of macro's call site.
You can insert (splice) a tree into a tree. You can not insert actual object into a tree. If you have actual object (compiled tree) it's too late to insert it into a tree.
When you see something being inserted into a tree, this means that "something" is just a compact way to write a tree i.e. an instance of type class
LiftableI guess your macro can look like
or
so you can call it like
foo(MyObject)orfoo[MyObject.type]and insideyou have access to
weakTypeOf[A], then its symbol. Having symbol you can have signatures of methods etc.Actually, in some sense there is a way to "insert" an object into a quasiquote. This is serialization/deserialization objects between stages
sbt clean compile runprintsm1(filefilemust exist, this doesn't work in IntelliJ).Similarly you can use @tribbloid's code in splain
Unfortunately this doesn't work with (case) objects because of a bug.
In multi-stage compilation, should we use a standard serialisation method to ship objects through stages?
https://contributors.scala-lang.org/t/in-multi-stage-compilation-should-we-use-a-standard-serialisation-method-to-ship-objects-through-stages/5699
https://github.com/EsotericSoftware/kryo
https://com-lihaoyi.github.io/upickle/#uPack
One more technique to define a macro if a value is from the next stage (and avoid cross-stage evaluation) is to construct a tree of function and then apply this function after macro expansion
Scala 2.13: Case class with extendable variable attributes?
Invoke a template Scala function with a type stored as wild card classTag?