Scala 3.1.3 released!

Tuesday 21 June 2022

Paweł Marks, VirtusLab

We are happy to announce the release of Scala 3.1.3! You can read more about all the improvements and fixes in the full changelog. We have also prepared a short list of highlights of the most exciting additions in the new version.

Highlights of the release

Improved f-interpolator

f-interpolator in Scala 3 has received multiple fixes and improvements recently. Now it has reached feature parity with its Scala 2 counterpart.

  • Cases that were incorrectly failing are now working as intended:

    f"${3.14}%.2f rounds to ${3}%d"           // "3.14 rounds to 3"
    f"${java.lang.Boolean.valueOf(false)}%b"  // "false"
    f"${BigInt(120)}%d"                       // "120"
    f"${3L}%e"                                // "3.000000e+00"
  • Backslash escapes are now handled properly and consistently:

    f"yes\\\no"   // "yes\
                  //  o"
  • Many cases that were throwing runtime exceptions are now causing compilation errors instead:

    f"{1} % y"  // Error: illegal conversion character 'y'

Better error reporting in inlined code

When the compiler reports an error that occurred in inlined code, it now displays the source from where the inlined function was invoked. That gives more context to the users on what happened and where the error came from.

For example, the snippet:

trait Foo[X]:
  def foo: Int

def foo =

inline def second[A]: Int =

inline def first[A]: Int =
  second[A] + 42

will now report a compilation error looking like this

the new error

instead of previously

the old error

Possibility to generate arbitrary class implementations in macros

For a long time, generating arbitrary classes was not possible using the quotes api. With 3.1.3, the situation has changed. We added two missing blocks: experimental methods ClassDef.apply and Symbol.newClass. Now using them, you can write a snippet similar to the following:

import scala.quoted.*

class Base

transparent inline def classNamed(inline name: String) = ${ classNamedExpr('name) }
def classNamedExpr(nameExpr: Expr[String])(using Quotes): Expr[Any] =
  import quotes.reflect.*

  val name = nameExpr.valueOrAbort
  val parents = List(TypeTree.of[Base])
  def decls(cls: Symbol) = List.empty  // put something interesting here
  val body = List.empty                // ...and here

  val symbol = Symbol.newClass(Symbol.spliceOwner, name,, decls, selfType = None)
  val classDef = ClassDef(symbol, parents, body)
  val ctor = Select(New(TypeIdent(symbol)), symbol.primaryConstructor)
  val newClass = Typed(Apply(ctor, Nil), TypeTree.of[Base])

  Block(List(classDef), newClass).asExprOf[Base]

Then you can invoke the macro with

val impl: Base = classNamed("Foo")

creating the new instance of your custom subclass of Base. That can be useful for the compile-time generation of proxies for remote procedure call systems and many other advanced use-cases.


Thank you to all the contributors who made this release possible.

According to git shortlog -sn --no-merges 3.1.2..3.1.3 these are:

    66  odersky
    48  Nicolas Stucki
    38  Filip Zybała
    36  Martin Odersky
    29  noti0na1
    27  Som Snytt
    24  Dale Wijnand
    19  Anatolii Kmetiuk
    19  Chris Kipp
    18  Paweł Marks
    17  Rikito Taniguchi
    13  Xavientois
    13  Tom Grigg
    12  Jan Chyb
    11  Guillaume Martres
     8  Jamie Thompson
     8  Matt Bovel
     6  Tomasz Godzik
     5  Michael Pilquist
     5  rochala
     4  adampauls
     4  Kacper Korban
     3  Sébastien Doeraene
     3  Andrzej Ressel
     3  Olivier Blanvillain
     3  Phil
     3  Seth Tisue
     2  Stéphane Micheloud
     2  Yichen Xu
     2  Arman Bilge
     2  Julien Richard-Foy
     2  Adrien Piquerez
     1  ireina7
     1  Alexander Ioffe
     1  Jentsch
     1  Jędrzej Rochala
     1  Michał Pałka
     1  Ondrej Lhotak
     1  Pascal Weisenburger
     1  Quentin Bernet
     1  Ruslan Shevchenko
     1  SrTobi
     1  Stephane MICHELOUD
     1  Vadim Chelyshov
     1  Vasil Vasilev
     1  bjornregnell
     1  ghostbuster91
     1  som-snytt
     1  Łukasz Wroński