It is known in scala 2 that macros are strictly local and are only executed once, when the class is defined. This feature seems particularly weak when combining with abstract type, as the process to convert abstract types into concrete one generally bypasses macro and uses its own primitive rules.
One simple example that demonstrates a counter-intuitive outcome is in the following test code:
trait BB {
def ttag = implicitly[TypeTag[this.type]]
}
case class AA() extends BB
it("can TypeTag") {
val kk = AA()
TypeViz(kk.ttag).peek // this function visualise the full type tree of the type tag
}
if executed, the type of kk turns out to be:
-+ BB.this.type
!-+ InfoCTSpec.this.BB
!-+ Object
!-- Any
Oops, the type AA is completely ignored, because implicitly[TypeTag[this.type]]
is backed by a built-in macro implicit, which is only executed ONCE when BB is defined, not when AA is defined and reify the actual kk.this.type
. I find it quite unwieldy and prone to cause some other features (e.g. pattern matching, type lambda) to degrade due to runtime type erasure.
I'd like to write/use a language extension to, e.g. make TypeTag[this.type]
a subtype of AA, WITHOUT introducing runtime overhead & out-of-scope context objects (so, NO IMPLICIT). How can I do this with the least amount of hacking? I'm open to very hardcore solutions like compiler extension and macro, but an elegant work-around that can be smoothly carried over to scala 3/dotty is obviously preferred.
P.S. it appears that the dotty "inline/compiletime" feature has partially implemented what I have envisioned. Is this the correct impression?
TypeViz.peek
? – Clipclop