This post is a followup of Roadmap towards non-experimental macros | The Scala Programming Language to initiate a discussion whether whitebox def macros should be included in an upcoming SIP proposal on macros. Please read the blog post for context.
Macro annotations have the ability to synthesize publicly available definitions, accommodating the public type provider pattern. Popular macro annotation libraries include
- simulacrum first-class syntax support for type classes in Scala
- Freestyle: cohesive & pragmatic framework of FP centric Scala libraries
I’m sure I’ve missed many other popular libraries using macro annotations.
To show an example macro annotation, consider the following @deriving
macro
annotation from the library Stalactite
@deriving(Encoder, Decoder)
case class User(name: String, age: Int)
// expands into
case class User(name: String, age: Int)
object User {
implicit val encoder: Encoder[User] = DerivedEncoder.gen
implicit val decoder: Decoder[User] = DerivedDecoder.gen
}
The unique feature of macro annotations is that they can synthesize publicly available definitions such as User.encoder/decoder
. Neither whitebox or blackbox def macros have this capability. It is generally considered best practice to place implicit typeclass instances in the companion object. This pattern significantly improves on compile times and prevents code bloat compared to full-derivation at each call-site.
Surely, it’s possible to manually write out the implicit val decoderBar: Decoder[Bar] = DerivedDecoder.gen
parts. However, for large business applications with many domain-specific data types and typeclasses, such boilerplate hurts readability and often falls prey to typos, leading to bugs.
Code generation via scripting can used as an alternative to macro annotations in many cases. It is not a perfect solution, code generation traditionally comes with a non-trivial build tax. Maybe it’s possible to address those limitations with better tools for scripted code generation, avoiding the need to include macro annotations in the Scala Language Specification.
What do you think, should macro annotations be included in the macros v3 SIP proposal? In particular, please try to answer the following questions
- towards what end do you use macro annotations?
- why are macro annotations important for you and your users?
- can you use alternative metaprogramming techniques such as
code generation scripts or compiler plugins to achieve the same
functionality? How would that refactoring impact your whitebox macro?