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 =
first[String]
inline def second[A]: Int =
compiletime.summonInline[Foo[A]].foo
inline def first[A]: Int =
second[A] + 42
will now report a compilation error looking like this
instead of previously
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, parents.map(_.tpe), 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.
Contributors
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