Scala 3 Migration Guide

Metaprogramming

Language

A call to a macro method is executed during the compiler phase called macro expansion to generate a part of the program—an abstract syntax tree.

The Scala 2.13 macro API is closely tied to the Scala 2.13 compiler internals. Therefore it is not possible for the Scala 3 compiler to expand any Scala 2.13 macro.

In contrast, Scala 3 introduces a new principled approach of metaprogramming that is designed for stability. Scala 3 macros, and inline methods in general, will be compatible with future versions of the Scala 3 compiler. While this is an uncontested improvement, it also means that all Scala 2.13 macros have to be rewritten from the ground up, using the new metaprogramming features.

Macro Dependencies

A Scala 3 module can depend on a Scala 2.13 artifact even if it contains a macro definition but the compiler will not be able to expand its macros. When you try to, it simply returns an error.

 -- Error: /src/main/scala/example/Example.scala:10:45 
 10 |  val documentFormat = Json.format[Document]
    |                            ^
    |Scala 2 macro cannot be used in Scala 3. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html
    |To turn this error into a warning, pass -Xignore-scala2-macros to the compiler

Let’s note that using -Xignore-scala2-macros is helpful to type check the code but it produces incomplete class files.

When this error appears in your project, you have eventually no other choice than upgrading to a Scala 3-compiled version of the macro artifact.

Porting the Macro Ecosystem

While being experimental, the Scala community has largely adopted the Scala 2 macro feature in multiple ways: code generation, optimizations, ergonomic DSLs…

A large part of the ecosystem now depends on Scala 2.13 macros defined in external libraries. Identifying and porting those libraries is key to move the ecosystem forward.

A migration status of many open-source macro libraries is available in this page.

Rewriting a Macro

The new metaprogramming features are completely different from Scala 2. They are comprised of:

Before getting deep into reimplementing a macro you should ask yourself:

  • Can I use inline and the scala.compiletime operations to reimplement my logic?
  • Can I use the simpler and safer expression-based macros?
  • Do I really need to access the AST?
  • Can I use a match type as return type?

You can learn all the new metaprogramming concepts by reading the Macros in Scala 3 tutorial.

Cross-building a Macro Library

You have written a wonderful macro library and you would like it to be available in Scala 2.13 and Scala 3. There are two different approaches, the traditional cross-building technique and the more flexible macro mixing technique.

The benefit of macro mixing is that consumers who take advantage of the -Ytasty-reader option can still use your macros.

You can learn about them by reading these tutorials:

Additional Resources

Blog posts and talks:

Early-adopter projects:

Contributors to this page: