Scala 3 is coming and will introduce many new features and changes while maintaining compatibility with Scala 2 code. The roadmap outlines stabilizing Scala 3.0 in fall 2019 and 2020, with Scala 2.x continuing in parallel. Key changes include replacing implicits with "givens", introducing enums, union types, and other features to improve the language for beginners, everyday usage, and experts. While significant, the changes are aimed at regularizing Scala and addressing longstanding issues like implicits in a way that eases migration from Scala 2 to 3 through common intermediate representations like Tasty.
Devoxx UK 2024 - Going serverless with Quarkus, GraalVM native images and AWS...
Scala 3 Is Coming: Martin Odersky Shares What To Know
1. Scala 3 Is Coming
By Martin Odersky
July 11th, 2019
2. Roadmap
June 2019 All features fleshed out, with
implementations in Dotty 0.16+
Fall 2019 Feature freeze, Scala 3.0 M1
stabilization
complete SIP process
write spec, user docs
migrate open-source ecosystem
flesh out tests, community build
compatibility and migration tools
Fall 2020 Scala 3.0 final
Scala 2.13
Scala 2.14
4. What Changes?
Docs for all changes and new features at
https://dotty.epfl.ch/docs/reference/overview.html
5. ?
Best of Scala 3
What are Scala-3’s Nicest Features?
• For beginners
• For everyday coding
• For experts
I’ll give my personal ranking
of the top 3 of each category.
I also did a Twitter survey and will report on that.
?
?
6. Nicest Features for Beginners?
• At that level, most of the language stays the same
• But there are nevertheless a few improvements worth
mentioning …
?
7. #3: Drop New
• new can be omitted from almost all instance creations.
• Only needed to disambiguate in an apply method.
• No need to define a case class anymore just to get nice
constructor calls.
class StringBuilder(s: String) {
def this() = this("")
}
StringBuilder("abc") // same as new StringBuilder("abc") StringBuilder() // same as new
StringBuilder()
8. #2: Toplevel Definitions
All kinds of definitions can be written on the toplevel.
Package objects are no longer needed, will be phased out.
package p
type Labelled[T] = (String, T)
val a: Labelled[Int] = ("count", 1)
def b = a._2
def hello(name: String) =
println(i"hello, $name)
9. #1 Enums
enum Color {
case Red, Green, Blue
}
Simplest way to define new types with a finite number of values or
constructors.
10. #1 Enums
can have parameters
can define fields and methods
can interop with Java
enum Planet(mass: Double, radius: Double)
extends java.lang.Enum {
private final val G = 6.67300E-11
def surfaceGravity = G * mass / (radius * radius)
case MERCURY extends Planet(3.303e+23, 2.4397e6)
case VENUS extends Planet(4.869e+24, 6.0518e6)
case EARTH extends Planet(5.976e+24, 6.37814e6)
case MARS extends Planet(6.421e+23, 3.3972e6)
...
}
11. #1 Enums
can have type parameters, making them algebraic data types (ADTs)
enum Option[+T] {
case Some(x: T)
case None
}
12. #1 Enums
compile to sealed hierarchies of case classes and singletons.
sealed abstract class Option[+T]
object Option {
case class Some[+T](x: T) extends Option[T]
val None = new Option[Nothing] { ... }
}
13. #1 Enums
can be GADTs (generalized ADTs).
cases can extend the base type with different type arguments.
enum Tree[T] {
case True extends Tree[Boolean]
case False extends Tree[Boolean]
case IsZero(n: Tree[Int]) extends Tree[Boolean]
case Zero extends Tree[Int]
case Succ(n: Tree[Int]) extends Tree[Int]
case If(cond: Tree[Boolean],
thenp: Tree[T],
elsep: Tree[T]) extends Tree[T]
}
15. Why Enums?
• Lots of use cases, and they are becoming more common.
• Avoids boring, repetitive boilerplate.
• Can grow from very simple to very powerful.
16. Nicest Features for Everyday Coding?
• There are lots of candidates.
• Hard to come up with a shortlist.
?
17. #3 Union Types
Provide ad-hoc combinations of types
Subsetting = Subtyping
No boxing overhead
case class UserName(name: String)
case class Password(hash: Hash)
def help(id: UserName | Password) = {
val user = id match {
case UserName(name) => lookupName(name)
case Password(hash) => lookupPassword(hash)
}
...
}
18. #3 Union Types
Work also with singleton types
Great for JS interop
type Command = ”Click" | ”Drag" | ”KeyPressed"
def handleEvent(kind: Command) = kind match {
case “Click" => MouseClick()
case ”Drag" => MoveTo()
case ”KeyPressed" => KeyPressed()
}
19. #2 Extension Methods
Replace implicit classes
Make it easier to add methods to existing classes.
object StringOps {
def (s: String) * (x: Int): String =
if (x <= 0) “” else s * (x - 1) ++ s
}
import StringOps._
"hello" * 3
20. #1 Givens
Replace “implicit” as a modifier
Express intent instead of mechanism
trait Ord[T] {
def (x: T) compareTo (y: T): Int
def (x: T) < (y: T) = x.compareTo(y) < 0
}
given IntOrd as Ord[Int] {
def (x: Int) compareTo (y: Int) =
if (x < y) -1 else if (x > y) +1 else 0
}
def maximum[T](xs: List[T]) given Ord[T] =
xs.reduce((x, y) => if (x < y) y else x)
21. New Design Principles
• Core concept: term inference.
• Given a type, produce a canonical term of that type
“Trade types for terms”
• Instead of adding implicit as a modifier to many different
constructs, changing the meaning of each in slightly
different ways, have a single construct to introduce a
designated term that can be inferred for a type.
• We call these terms “given instances” or just “givens”.
22. Typeclasses
Givens work out of the box with extension methods,
provide a nice syntax for typeclasses
trait SemiGroup[T] {
def (x: T) combine (y: T): T
}
trait Monoid[T] extends SemiGroup[T] {
def unit: T
}
given as Monoid[String] {
def (x: String) combine (y: String) = x.concat(y)
def unit = ""
}
def sum[T: Monoid](xs: List[T]): T =
xs.foldLeft( the[Monoid[T]].unit )(_.combine(_))
23. Implicit Conversions
implicit def intToStr(str: String): Token =
new Keyword(str)
Givens tame implicit conversions
What’s wrong with this?
It’s too easy to write compared to how dangerous it is.
Implicit as a modifier will go away, and with it this kind of conversion.
24. Conversion Instances
The only way to express implicit conversions is as a given instance
of a standard Conversion class:
given as Conversion[String, Token] {
def apply(str: String): Token = new KeyWord(str)
}
Or, using an alias:
given as Conversion[String, Token] =
new KeyWord(_)
26. Why “Given”?
Implicits are Scala’s most distinguished feature.
But they are also the most controversial one.
Given instances are a simpler and safer alternative. They
• emphasize intent over mechanism
• make idea of term inference more accessible
• discourage abuses
27. Why “Given”?
Many improvements over current implicits, including:
• names don’t matter
• nesting is significant local coherence is easy
• no shadowing problems
• restricted implicit scope avoids surprises
• more robust prioritization
• no accidental conversions
• better error messages
More details in my Typelevel Lausanne keynote
28. Migration
• Current implicits are still supported in Scala 3.0
• Will probably be deprecated from Scala 3.1 on.
• Special provisions are made for cross compilation:
- given arguments also work for old style implicits
- given imports also import old style implicits
• This allows to migrate user code before libraries are moved over.
29. The Hardest Thing…
… was naming them!
The design of delegates has been solid for 6 months,
but how do we call these terms that get inferred for types?
We went through a long list of names:
In the end, it will not matter, just need a noun that’s easy to remember.
witness evidence
instance
impl
repr
assume
representative
delegate
implied
implicit
30. Nicest Features for Experts?
• Since Scala 3 puts Meta Programming on a new basis, there is
lots to choose from.
• Difficult to pick a winner.
?
31. #3 Match Types
• Match types can be recursive.
• More straightforward than using implicits (c.f. HLists).
• Work in type checking is still in progress
enum Tuple {
case Empty
case Pair[Hd, Tl]
}
import Tuple._
type Concat[+Xs <: Tuple, +Ys <: Tuple] <: Tuple =
Xs match {
case Empty => Ys
case Pair[x, xs] => Pair[x, Concat[xs, Ys]]
}
32. #2 Typeclass Derivation
enum Tree[T] derives Eql, Ordering, Pickling {
case Branch(left: Tree[T], right: Tree[T])
case Leaf(elem: T)
}
given [T: Eql] as Eql[Tree[T]] = Eql.derived
given [T: Ordering] as Ordering[Tree[T]] = Ordering.derived
given [T: Pickling] as Pickling[Tree[T]] = Pickling.derived
this generates:
where typeclasses define “derived” methods.
ScalaDays Talk by Miles Sabin
33. #2 Typeclass Derivation
“derived” methods use match types, inline and macros to do their work
object Eq {
inline def derived[T] given (mirror: Mirror.Of[T]): Eq[T] =
new Eq[T] {
def eql(x: T, y: T): Boolean =
inline mirror match {
case m: Mirror.SumOf[T] =>
val ord = m.ordinal(x)
ord == m.ordinal(y) && eqlCases[m.ElemTypes](0)(x, y, ord)
case m: Mirror.ProductOf[T] =>
eqlElems[m.ElemTypes](0)(x, y)
}
}
ScalaDays Talk by Nicolas Stucki
34. #1 Functions Everywhere
Scala distinguishes methods from functions.
Scala 3 lifts three fundamental capabilities from one to the other.
Function types can be:
• dependent:
• polymorphic:
• implicit:
trait Graph { type Node; type Edge }
type NodeExtractor = (g: Graph) => g.Node
type PolyIdentity = [T] => T => T
type Executable[T] = given ExecutionContext => T
38. Is Scala 3 a New Language?
Yes!
• Many language changes, including feature removals.
• New constructs improve user experience and on-boarding.
• Books will have to be rewritten.
39. Is Scala 3 a New Language?
No!
• It’s still Scala.
• All core constructs remain in place.
• Large & practical common subset between Scala 2 and Scala 3.
• Programs can cross-build
Dotty compiler, ScalaTest.
40. Is Scala 3 a New Language?
It’s a Process
• Scala 3.0 keeps most constructs of 2.13, alongside the new ones.
• Some constructs will be deprecated and phased out in the 3.x
release train.
• This requires some temporary duplication.
• End result: a more compact and regular language.
41. Replacements
Exports + toplevel defs for Package objects
Givens for Implicit defs, vals, objects,
conversions
Extension methods for Implicit classes
Inline + staging + for Current macros
match types
42. Why So Many New Features At Once?
Scala 3 is when the
books will be rewritten.
Need to get it in now if
• it affects foundations,
• it simplifies life,
especially for
learners,
• it replaces existing
features.
43. Why So Many New Features At Once?
Hence, prioritize
- Foundations
- Simplifications
(for developers)
- Restrictions
over added power and
expressiveness
44. How to Get There?
• Source compatibility for a large common subset.
• Rewrite tools can handle much of the rest
• Situation better than for Python 2 vs 3 because of static typing &
binary compatibility.
Talk by Lukas Rytz, Wed 16.45
46. Binary Compatibility
today:
Dotty can link with
Scala-2.12 class files.
Scala 2 module
Dotty module
in the works:
Two way compatibility
using Tasty as common
intermediate format.
Scala 2 module
Scala 3 module
47. Tasty At The Core
Tasty
.scala (2.x)
.scala (3.x)
.class (Java 8)
.js
.class (Java 11)
.out
macros
analyzers
optimizers
IDE
LSP
Talk by Gullaume Martres, Wed 10.15
48. Binary Compatibility For Scala 3
• The plan is to keep the Tasty format binary compatible
over the whole 3.x series.
• Compile-from Tasty then allows code to migrate without
the current problems of binary compatibility.
OPEN GAMBIT
Multicore. Cloud computing. Containers. You’ll likely agree that the infrastructure for amazing scalability is in place, it’s been well funded. It’s the underpinning that’s required for enterprises to movie en masse to the cloud. But what are they moving?
Applications.
Applications that run their business, engage their customers, allow them to innovate and enter new markets.
Without applications, this infinitely scalable infrastructure is nothing.