Open gzoller opened 1 month ago
Since macros are the only access to reflected class information in Scala 3, it's an "ism" we'll all have to live with, until such time as someone invents a way to track macro dependencies in sbt.
Zinc actually do... track macro dependencies (kinda), with a fully fledged development roadmap back in 2014 and partial implementations of the roadmap scattered around the years, with the latest in https://github.com/sbt/zinc/pull/1282, https://github.com/sbt/zinc/pull/1316. The example mentioned in the article
// File1.scala
case class Foo(name: String)
// File2.scala
val fooRType = RType.of[Foo]
actually works if one uses Scala 2.12 and latest sbt version. (assuming RType.of[T]
is a macro that reflectively inspect T
).
I would say that macro invalidation is practically a solved problem if one sufficiently familiar with Macro Engine is willing to give it a last push. The only puzzle piece missing is tracking symbols touched reflectively during macro reflection, and a 2014 POC showed that it is feasible. (Just that 2014 POC did it in a really hacky way, so it was unmerged).
You mention Scala 2 macros. Is this partial capability also present for Scala 3 macros? So far I haven’t seen that anything picks up changes in the macro vs generated code, so its very easy to get in a situation where you change the code your macro expands over (ex: RType.of[Foo] where Foo has just changed) yet the macro is not re-expanded. You still have expanded code for the old, pre-change Foo. It’ll all compile fine and explode quite dramatically when run, usually with an obscure error that means nothing to the uninitiated. In an ideal world, zinc would detect Foo’s change and when it happened upon RType.of[Foo] would re-run the macro expansion, giving a seamless experience.
You mention Scala 2 macros. Is this partial capability also present for Scala 3 macros? So far I haven’t seen that anything picks up changes in the macro vs generated code, so its very easy to get in a situation where you change the code your macro expands over (ex: RType.of[Foo] where Foo has just changed) yet the macro is not re-expanded. You still have expanded code for the old, pre-change Foo. It’ll all compile fine and explode quite dramatically when run, usually with an obscure error that means nothing to the uninitiated. In an ideal world, zinc would detect Foo’s change and when it happened upon RType.of[Foo] would re-run the macro expansion, giving a seamless experience.
Not yet. Currently the partial capability is only available for Scala 2.12. Still need to port to Scala 2.13 and Scala 3. I plan to do it soon.
It would be amazing if incremental compilation was aware of macro dependencies—changes in code generated by a Scala 3 macro. The problem is described here: https://github.com/gzoller/scala-reflection#learning-to-drive-with-macros
If this could be made to work, code using macros would be as seamless as non-macro code with respect to compilation.