

When writing any non-trivial macro, you'll need to become at least a bit familiar with the reflect AST. To compare, using the default printer we would get: ((s: ) => s.length()) The AST after a while the above representation becomes semi-readable Inlined(None, Nil, Block(Nil, Block(List(DefDef("$anonfun", List(TermParamClause(List(ValDef("s", TypeIdent("String"), None)))), Inferred(), Some(Block(Nil, Apply(Select(Ident("s"), "length"), Nil))))), Closure(Ident("$anonfun"), None))))

For example, here's a fragment of the helper class for working with case classes in tapir: // more on Type, TypeRepr, Symbol etc. If you'd like to create a helper class that is used across multiple macros, things are a bit more complex.
Scala quicklens code#
Quoting is converting Scala code into an Expr, and is written down using '.asTerm asExprOf, if you have access to the precise representation of the type T. However, to splice a term into a code block, you'll need to first convert it to an expression, using. Here you have access to the whole AST, which you can inspect, or generate by hand. Terms, on the other hand, are the lower-level representation. You can't do much with it, other than splicing it and thus combining it with other code. In general, Expr represents an opaque, typed expression. When writing macros, you'll find yourself working a lot with values of type Expr and Tree/ Term.

The linked documentation is a great starting point, still, there's a number of things that come up when you start working with the macros API. If none of the above meets your needs, there’s no escape: you'll have to write a macro. using the recently released Shapeless 3, which is driven by givens and implicit resolution.using Magnolia, where you implement a simple interface for handling products & coproducts, and this is then used by the Magnolia macro.using inlines & mirrors, see the docs, and this blog which provides a great explanation of the involved concepts.

If the derivation that you'd like to perform has a regular structure, you might be able to use one of the available high-level methods for typeclass derivation in Scala: If you're convinced that inline won't work for your use case, there's still a chance you might escape the need of writing a macro! In a lot of cases, macros are used to perform derivation: generating code based on the structure of your data. Here, we will focus on slightly more advanced topics. As an introduction to writing macros, check out my other article, " Starting with Scala 3 macros: a short tutorial". Hence, you might turn to macros: they give you way more power and way more flexibility, at the cost of being more complex to write. Only partial information on data structures is available through Mirrors - for example annotations are not available, and if your data is not a case class, a mirror usually won't be available. It's not possible to inspect code passed as a parameter to an inline method or generate arbitrary code as a result. They are declarative in their nature, hence the possibilities are limited to what is directly supported by the compiler. Inlines can get you so far, but have their limits. The official documentation has more details on how inlines work and what their possibilities are. Through Mirrors, you can inspect data structures, such as case classes and enums. This includes expanding inline def method definitions, simplifying inline if and inline match expressions, or performing inline summon (looking up given/ implicit values). The inline modifier instructs the compiler to evaluate the code at compile-time. Scala 3 offers two basic metaprogramming modes: inlines or macros. The first question to consider is: Do I have to write a macro in the first place? Below, you can find some of the things that we’ve learned along the way. We’ve ported a number of macros from Scala 2 to Scala 3: in quicklens, with Kacper Korban in tapir, with Mateusz Borek and more work is waiting with macwire. As with every API, it takes getting used to Scala’s compile-time reflection capabilities are no exception. Scala 3 offers a brand new metaprogramming experience with a brand new API.
