SlideShare une entreprise Scribd logo
1  sur  126
Télécharger pour lire hors ligne
What's new in Scala 2.13?
© 2019 Hermann Hueck
https://github.com/hermannhueck/new-in-scala213
1 / 125
1 / 125
Abstract
This presentation shows the feature updates from Scala 2.12 to 2.13. The list of
features is not comprehensive, but it is my personal selection of favorites. I
will focus on those which IMO impact/ease the programmers live most.
I will look at 5 feature areas: compiler, standard library, language changes,
Future and finally the most important change the redesigned collections
library.
I will not only show the new features of 2.13. In many cases I will show how
the new features of 2.13 can be backported to 2.12 und be used in mostly the
same way as in 2.13.
Finally I'll give some guide lines for the migration from 2.12 to 2.13 and for a
cross version project which compiles a code base with both compiler versions.
2 / 125
2 / 125
Agenda
1. Release Summary
2. Compiler
3. sbt Setup for Cross Compilation
4. Standard Library
5. Concurrency / Future
6. Language Changes
7. Collections
8. Architecture of Collections
9. Migration
10. Resources
3 / 125
3 / 125
1. Release Summary
4 / 125
4 / 125
Release Summary
Release 2.13 improves Scala in the following areas:
Collections: Standard library collections have been overhauled for
simplicity, performance, and safety. This is the centerpiece of the release.
5 / 125
5 / 125
Release Summary
Release 2.13 improves Scala in the following areas:
Collections: Standard library collections have been overhauled for
simplicity, performance, and safety. This is the centerpiece of the release.
Future: is faster and more robust.
6 / 125
6 / 125
Release Summary
Release 2.13 improves Scala in the following areas:
Collections: Standard library collections have been overhauled for
simplicity, performance, and safety. This is the centerpiece of the release.
Future: is faster and more robust.
Standard library: New classes have been added and new methods
enhance existing classes. But also some deprecations.
7 / 125
7 / 125
Release Summary
Release 2.13 improves Scala in the following areas:
Collections: Standard library collections have been overhauled for
simplicity, performance, and safety. This is the centerpiece of the release.
Future: is faster and more robust.
Standard library: New classes have been added and new methods
enhance existing classes. But also some deprecations.
Language: Literal types, partial unification is default, by-name implicits,
more.
8 / 125
8 / 125
Release Summary
Release 2.13 improves Scala in the following areas:
Collections: Standard library collections have been overhauled for
simplicity, performance, and safety. This is the centerpiece of the release.
Future: is faster and more robust.
Standard library: New classes have been added and new methods
enhance existing classes. But also some deprecations.
Language: Literal types, partial unification is default, by-name implicits,
more.
Compiler: 5-10% faster, deterministic output, improved optimizer.
9 / 125
9 / 125
Release Summary
Release 2.13 improves Scala in the following areas:
Collections: Standard library collections have been overhauled for
simplicity, performance, and safety. This is the centerpiece of the release.
Future: is faster and more robust.
Standard library: New classes have been added and new methods
enhance existing classes. But also some deprecations.
Language: Literal types, partial unification is default, by-name implicits,
more.
Compiler: 5-10% faster, deterministic output, improved optimizer.
Scala 2.13.0: https://github.com/scala/scala/releases/tag/v2.13.0
Scala 2.13.1: https://github.com/scala/scala/releases/tag/v2.13.1
10 / 125
10 / 125
2. Compiler
11 / 125
11 / 125
Compiler
Performance improved
Some improvments already flew back into 2.12.8, 2.12.9, 2.12.10.
Deterministic, reproducible compilation
Optimizer improvements (collections, arrays, inlining)
12 / 125
12 / 125
Macro Annotations
There is no more "macro paradise" compiler plugin for 2.13.
Instead, macro annotations are handled directly by the compiler.
Macro annotations are enabled with the -Ymacro-annotations flag.
Macro annotations remain experimental.
13 / 125
3. sbt Setup for Cross
Compilation
14 / 125
13 / 125
14 / 125
Cross compile with sbt
// build.sbt
inThisBuild(
Seq(
scalaVersion := "2.13.1",
crossScalaVersions := List("2.12.10", "2.13.1"),
...
)
)
15 / 125
15 / 125
Cross compile with sbt
// build.sbt
inThisBuild(
Seq(
scalaVersion := "2.13.1",
crossScalaVersions := List("2.12.10", "2.13.1"),
...
)
)
scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, minor)) if minor >= 13 =>
Seq.empty
case _ =>
Seq(
"-Ypartial-unification", // (removed in scala 2.13) allow the Compiler to
// unify type constructors of different arities
"-language:higherKinds", // (not required since scala 2.13.1) suppress
// warnings when using higher kinded types
)
}
}
16 / 125
16 / 125
3. Standard Library
17 / 125
17 / 125
Smaller Footprint
No longer included in the stdlib:
scala-parallel-collections
scala-xml
scala-parser-combinators
scala-swing
These become libraries of their own.
18 / 125
18 / 125
Integrated Java Interop
The old scala-java8-compat module is now part of the standard library.
This provides converters for options, function types and Java streams.
scala.collection.JavaConversions removed (already deprecated in 2.12).
19 / 125
19 / 125
Chaining: pipe and tap
import scala.util.chaining._
val x: Int = 5 tap println
val y: Int = 5 pipe (_ * x) tap println
List(1, 2, 3) pipe (ys => println("debug: " + ys.toString))
val times6 = (_: Int) * 6
(1 - 2 - 3)
.tap(v => println(s"initial: $v"))
.pipe(times6)
.tap(v => println(s"after times6: $v"))
.pipe(scala.math.abs)
.pipe(v => println(s"after scala.math.abs: $v"))
20 / 125
20 / 125
Chaining: pipe and tap
import scala.util.chaining._
val x: Int = 5 tap println
val y: Int = 5 pipe (_ * x) tap println
List(1, 2, 3) pipe (ys => println("debug: " + ys.toString))
val times6 = (_: Int) * 6
(1 - 2 - 3)
.tap(v => println(s"initial: $v"))
.pipe(times6)
.tap(v => println(s"after times6: $v"))
.pipe(scala.math.abs)
.pipe(v => println(s"after scala.math.abs: $v"))
x pipe f is a replacement for f(x), where f is a Function1[A => B].
x tap f is a replacement for x => { f(x); x }, where f is a side-effecting
Function1[A => Unit]. tap performs the side effect in f(x) and returns x
unchanged.
21 / 125
21 / 125
Backport to 2.12: pipe and tap
My 2.12 backport library uses the same package name as in 2.13 stdlib:
scala.util.chaining
package scala.util
package object chaining {
implicit class ChainingOps[A](private val a: A) {
@inline def pipe[B](f: A => B): B = f(a)
@inline def tap[B](f: A => Unit): A = { f(a); a }
}
}
22 / 125
22 / 125
Backport to 2.12: pipe and tap
My 2.12 backport library uses the same package name as in 2.13 stdlib:
scala.util.chaining
package scala.util
package object chaining {
implicit class ChainingOps[A](private val a: A) {
@inline def pipe[B](f: A => B): B = f(a)
@inline def tap[B](f: A => Unit): A = { f(a); a }
}
}
With this backport he same code runs without friction under 2.12 and 2.13.
23 / 125
23 / 125
Either: Right.withLeft and Left.withRight
Right.apply leaves the left type unspecified.
Left.apply leaves the right type unspecified.
Right.withLeft and Left.withRight let you specity the unspecified type.
24 / 125
24 / 125
Either: Right.withLeft and Left.withRight
Right.apply leaves the left type unspecified.
Left.apply leaves the right type unspecified.
Right.withLeft and Left.withRight let you specity the unspecified type.
sbt:New in Scala 2.13> ++2.13.0 console
...
Welcome to Scala 2.13.0 ...
scala> Right(5)
res0: scala.util.Right[Nothing,Int] = Right(5)
scala> Right(5).withLeft[String]
res1: scala.util.Either[String,Int] = Right(5)
scala> Left("some error")
res2: scala.util.Left[String,Nothing] = Left(some error)
scala> Left("some error").withRight[Int]
res3: scala.util.Either[String,Int] = Left(some error)
25 / 125
25 / 125
Backport to 2.12:
Right.withLeft and Left.withRight
package compat213
package object either {
implicit class RightOps[L, R](private val right: Right[L, R]) {
@inline def withLeft[LL >: L]: Either[LL, R] = right
}
implicit class LeftOps[L, R](private val left: Left[L, R]) {
@inline def withRight[RR >: R]: Either[L, RR] = left
}
}
26 / 125
26 / 125
Either#flatten
available since 2.13
Either#flatten is equivalent to Either#flatMap(x => x)
27 / 125
27 / 125
Scala 2.12 Scala 2.13
val rr = Right(Right(42))
rr.flatten //=> Right(42)
val rl = Right(Left("Error RL"))
rl.flatten //=> Left("Error RL")
val l = Left("Error L")
l.flatten //=> Left("Error L")
val ll = Left(Left("Error LL"))
ll.flatten //=> Left(Left("Error LL"))
Either#flatten
available since 2.13
Either#flatten is equivalent to Either#flatMap(x => x)
val rr = Right(Right(42))
rr.flatMap(x => x) //=> Right(42)
val rl = Right(Left("Error RL"))
rl.flatMap(x => x) //=> Left("Error RL")
val l = Left("Error L")
l.flatMap(x => x) //=> Left("Error L")
val ll = Left(Left("Error LL"))
ll.flatMap(x => x) //=> Left(Left("Error LL"))
scaladoc: Either
28 / 125
28 / 125
Backport to 2.12: Either#flatten
package compat213
package object either {
implicit class EitherOps[+L, +R](private val either: Either[L, R]) {
@inline def flatten[L1 >: L, RR](
implicit ev: R <:< Either[L1, RR]
): Either[L1, RR] =
either.flatMap(x => x)
}
}
29 / 125
29 / 125
String Operations: toIntOption etc.
convert String literals to Int, Double, Boolean without throwing exceptions
return Some(value) if the conversion succeeds, None if it fails.
30 / 125
30 / 125
String Operations: toIntOption etc.
convert String literals to Int, Double, Boolean without throwing exceptions
return Some(value) if the conversion succeeds, None if it fails.
sbt:New in Scala 2.13> ++2.13.0 console
...
Welcome to Scala 2.13.0 ...
scala> "42".toIntOption
res0: Option[Int] = Some(42)
scala> "42.0".toIntOption
res1: Option[Int] = None
scala> "42.0".toDoubleOption
res2: Option[Double] = Some(42.0)
scala> "true".toBooleanOption
res3: Option[Boolean] = Some(true)
31 / 125
31 / 125
Backport to 2.12: String Operations
package compat213
package object string {
implicit class StringOps(private val s: String) {
import scala.util.Try
@inline def toIntOption: Option[Int] = Try(s.toInt).toOption
@inline def toDoubleOption: Option[Double] = Try(s.toDouble).toOption
@inline def toBooleanOption: Option[Boolean] = Try(s.toBoolean).toOption
}
}
32 / 125
32 / 125
scala.util.Using.apply for resource management
Similar to try ... catch ... finally,
but guarantees to release/close the used resource.
33 / 125
33 / 125
scala.util.Using.apply for resource management
Similar to try ... catch ... finally,
but guarantees to release/close the used resource.
def bufferedReader(fileName: String): BufferedReader =
new BufferedReader(new FileReader(fileName))
def readLines(reader: BufferedReader): Seq[String] =
??? // some impl
def tryLines(fileName: String): Try[Seq[String]] =
Using(bufferedReader(fileName)) { reader => readLines(reader) }
def catFile(fileName: String): Unit =
tryLines(fileName) match {
case Failure(exception) => exception.toString pipe println
case Success(lines) => lines foreach println
}
catFile("README.md")
34 / 125
34 / 125
scala.util.Using.resource
returns an A, not a Try[A]
35 / 125
35 / 125
scala.util.Using.resource
returns an A, not a Try[A]
package scala.util
object Using { // simplified
def apply[R, A](resource: => R)(f: R => A): Try[A]
def resource[R, A](resource: => R)(f: R => A): A
...
}
36 / 125
36 / 125
scala.util.Using.resource
returns an A, not a Try[A]
package scala.util
object Using { // simplified
def apply[R, A](resource: => R)(f: R => A): Try[A]
def resource[R, A](resource: => R)(f: R => A): A
...
}
def bufferedReader(fileName: String): BufferedReader = ???
def readLines(reader: BufferedReader): Seq[String] = ???
def lines(fileName: String): Seq[String] =
Using.resource(bufferedReader(fileName))(readLines)
def catFile2(fileName: String): Unit = { // might throw an exception
lines(fileName) foreach println
}
catFile2("README.md")
37 / 125
37 / 125
Backport to 2.12: Using
package compat213
import scala.util.Try
import scala.language.reflectiveCalls
object Using {
type Closable = { def close(): Unit }
def apply[A, R <: Closable](resrc: R)(use: R => A): Try[A] =
Try(resource(resrc)(use))
def resource[A, R <: Closable](resrc: R)(use: R => A): A =
try {
use(resrc)
} finally {
resrc.close()
}
}
38 / 125
38 / 125
Backport to 2.12: Using
package compat213
import scala.util.Try
import scala.language.reflectiveCalls
object Using {
type Closable = { def close(): Unit }
def apply[A, R <: Closable](resrc: R)(use: R => A): Try[A] =
Try(resource(resrc)(use))
def resource[A, R <: Closable](resrc: R)(use: R => A): A =
try {
use(resrc)
} finally {
resrc.close()
}
}
This impl is a bit simplistic, but should work for resources which provide a
method close.
39 / 125
39 / 125
s-Interpolator in Pattern matches
val dateString = "11-June-2019"
val s"$day-$month-$year" = dateString
year pipe println
month pipe println
day pipe println
40 / 125
40 / 125
Named Product Elements
Products (i.e. case classes and Tuples) now have methods
productElementNames and productElementName.
41 / 125
41 / 125
Named Product Elements
Products (i.e. case classes and Tuples) now have methods
productElementNames and productElementName.
sealed trait Gender extends Product with Serializable
case object Male extends Gender
case object Female extends Gender
case class Person(name: String, age: Int, gender: Gender, email: String) {
def tupled: (String, Int, Gender, String) = Person.unapply(this).get
}
val johndoe = Person("John Doe", 42, Male, "john@doe.com")
42 / 125
42 / 125
Named Product Elements
Products (i.e. case classes and Tuples) now have methods
productElementNames and productElementName.
sealed trait Gender extends Product with Serializable
case object Male extends Gender
case object Female extends Gender
case class Person(name: String, age: Int, gender: Gender, email: String) {
def tupled: (String, Int, Gender, String) = Person.unapply(this).get
}
val johndoe = Person("John Doe", 42, Male, "john@doe.com")
johndoe.productElementNames foreach println
johndoe.productElementName(0) pipe (name => print(s"$name: "))
johndoe.productElement(0) pipe println
johndoe.productElementName(1) pipe (name => print(s"$name: "))
johndoe.productElement(1) pipe println
43 / 125
43 / 125
Named Product Elements
Naive JSON Serialization
def pairToJson(name: String, value: Any): String =
s"""{ "$name": $value }"""
def productElementToJson(p: Product, index: Int): String =
pairToJson(p.productElementName(index), p.productElement(index))
def productToJson(product: Product): String =
(0 until product.productArity)
.toList
.map { index => productElementToJson(product, index) }
.mkString("{ ", ", ", " }")
implicit class ProductOps(private val product: Product) {
def toJsonString: String = productToJson(product)
}
johndoe.toJsonString pipe println
// { { "name": John Doe }, { "age": 42 }, { "gender": Male }, { "email": john@doe.com } }
johndoe.tupled.toJsonString pipe println
// { { "_1": John Doe }, { "_2": 42 }, { "_3": Male }, { "_4": john@doe.com } }
44 / 125
44 / 125
4. Concurrency / Future
45 / 125
45 / 125
Future + ExecutionContext Changes Overview
API nearly unchanged
Massive performance improvements under the hood (Future, Promise,
ExecutionContext)
Improved handling of failures (InterruptedException,
RejectedExecutionException)
Made the global ExecutionContext “batched”
Added synchronous ("parasitic") ExecutionContext (releases you from
writing your own synchronous ExecutionContext)
For more details on the internals of the improved implementation see Viktor Klang's
talk at Scala Days 2019: Making Our Future Better
https://www.youtube.com/watch?v=5FTJUUoT6y4
46 / 125
46 / 125
Future : Minor API Changes
removed onSuccess and onFailure* (already deprecated in 2.12)
Future.delegate - new factory method
47 / 125
47 / 125
Future.delegate
object Future {
def apply[T](body: => T)(implicit executor: ExecutionContext): Future[T]
def delegate[T](body: => Future[T])(implicit executor: ExecutionContext): Future[T]
...
}
48 / 125
48 / 125
Future.delegate
The following expressions are semantically equivalent:
def expr[T]: Future[T] = ???
val f1 = Future.delegate(expr)
val f2 = Future.apply(expr).flatten
val f3 = Future.unit.flatMap(_ => expr)
object Future {
def apply[T](body: => T)(implicit executor: ExecutionContext): Future[T]
def delegate[T](body: => Future[T])(implicit executor: ExecutionContext): Future[T]
...
}
49 / 125
49 / 125
Future.delegate - Example
import scala.concurrent._
import scala.concurrent.duration._
import scala.util.chaining._
implicit lazy val ec: ExecutionContext = ExecutionContext.global
def plus17(x: Int): Int = x + 17
def squaredAsync(value: Int) = Future { value * value }
val f1: Future[Int] = Future.apply { squaredAsync(5) }.flatten map plus17
val f2: Future[Int] = Future.unit.flatMap { _ => squaredAsync(5) } map plus17
val f3: Future[Int] = Future.delegate { squaredAsync(5) } map plus17
Await.result(f1, 3.seconds) pipe println //=> 42
Await.result(f2, 3.seconds) pipe println //=> 42
Await.result(f3, 3.seconds) pipe println //=> 42
50 / 125
50 / 125
5. Language Changes
51 / 125
51 / 125
Language Changes Overview
Literal types: Literals (for strings, integers etc.) now have associated
literal types.
Partial unification: enabled by default
By-name implicit parameters: enable implicit search to construct
recursive values.
Underscores in numeric literals
Procedure syntax deprecated:
Deprecated: def m() { ... } Use instead: def m(): Unit = { ... }
View bounds deprecated:
Deprecated: A <% B Use instead: (implicit ev: A => B)
Symbol literals deprecated:
Deprecated: 'foo Use instead: Symbol("foo")
New Tuple2 arrow syntax in pattern match:
2.12 and 2.13: case (x, y) => ...
2.13 only : case x -> y => ...
52 / 125
52 / 125
Underscores in Number Literals
val int0: Int = 1000000
val int1: Int = 1_000_000
val int2: Int = 1_0_0_0_0_0_0
// val int3: Int = 1_0_0_0_0_0_0_
// compile error: trailing separator is not allowed
val long: Long = 1_000_000_000L
val float: Float = 1_000.99f
val double: Double = 1_000_000.999_999
53 / 125
53 / 125
Partial unification
Partial unification is enabled by default in 2.13.
The compiler no longer accepts -Ypartial-unification.
The following code compiles in 2.12 only with -Ypartial-unification.
54 / 125
54 / 125
Partial unification
Partial unification is enabled by default in 2.13.
The compiler no longer accepts -Ypartial-unification.
The following code compiles in 2.12 only with -Ypartial-unification.
// import scala.language.higherKinds // redundant since 2.13.1
def foo[F[_], A](fa: F[A]): String =
fa.toString
val either: Either[String, Int] = Right(42).withLeft[String]
foo { either }
val intToInt: Function1[Int, Int] = x => x * 2
foo { intToInt }
55 / 125
55 / 125
Partial unification
Partial unification is enabled by default in 2.13.
The compiler no longer accepts -Ypartial-unification.
The following code compiles in 2.12 only with -Ypartial-unification.
// import scala.language.higherKinds // redundant since 2.13.1
def foo[F[_], A](fa: F[A]): String =
fa.toString
val either: Either[String, Int] = Right(42).withLeft[String]
foo { either }
val intToInt: Function1[Int, Int] = x => x * 2
foo { intToInt }
Detailed explanation of partial unification here:
https://gist.github.com/djspiewak/7a81a395c461fd3a09a6941d4cd040f2
56 / 125
56 / 125
scalacOptions in build.sbt for cross compilation
scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, minor)) if minor >= 13 =>
Seq.empty
case _ =>
Seq(
"-Ypartial-unification", // (removed in scala 2.13) allow the Compiler to
// unify type constructors of different arities
"-language:higherKinds" // (not required since scala 2.13.1) suppress
// warnings when using higher kinded types
)
}
}
57 / 125
57 / 125
Literal Types
Literals (for strings, integers etc.) now have associated literal types.
The compiler will provide instances of a new typeclass scala.ValueOf[T]
for all singleton types T.
The value of a singleton type can be accessed by calling method
valueOf[T].
58 / 125
58 / 125
Literal Types
Literals (for strings, integers etc.) now have associated literal types.
The compiler will provide instances of a new typeclass scala.ValueOf[T]
for all singleton types T.
The value of a singleton type can be accessed by calling method
valueOf[T].
val wahr: true = true
val foo: "foo" = "foo"
val one: 1 = 1
val other_one: one.type = one
implicitly[other_one.type =:= 1]
val x1: Int = valueOf[42] // valueOf[42] yields an Int and is the same as ...
val x2: Int = new scala.ValueOf(42).value
59 / 125
59 / 125
By-name Implicit Parameters
were not allowed in 2.12.
They enable implicit search to construct recursive values.
The following code will not compile
if you remove the => in (implicit rec: => Foo) .
60 / 125
60 / 125
By-name Implicit Parameters
were not allowed in 2.12.
They enable implicit search to construct recursive values.
The following code will not compile
if you remove the => in (implicit rec: => Foo) .
trait Foo {
def next: Foo
}
object Foo {
// wouldn't compile, if rec were a call by value parameter
// remove the => and try to compile ...
implicit def foo(implicit rec: => Foo): Foo =
new Foo { def next = rec }
}
val foo = implicitly[Foo]
assert(foo eq foo.next)
61 / 125
61 / 125
6. Collections
62 / 125
62 / 125
Principles of the Collections Redesign
simplicity
better error messages
easier to implement your own collection (but still complex)
performance
type safety, better type inference
smaller footprint: parallel collections moved to a module of it's own, etc.
source code compatibility - as much as possible
Most ordinary code that used the old collections will continue to work as-
is. But of course ... there are breaking changes.
63 / 125
63 / 125
Simpler Method Signatures
No more CanBuildFrom
Without CanBuildFrom method signatures became much simpler.
64 / 125
64 / 125
Simpler Method Signatures
No more CanBuildFrom
Without CanBuildFrom method signatures became much simpler.
List#map in 2.12
List#map in 2.13
trait List[+A] extends ... {
def map[B](f: A => B): List[B] = ???
}
trait List[+A] extends ... {
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That = ???
}
65 / 125
65 / 125
Simpler Type Hierarchy
No more Traversable and TraversableOnce.
They remain only as deprecated aliases for Iterable and IterableOnce.
Parallel collections are now a separate module.
As a result, GenSeq, GenTraversableOnce, et al. are gone.
66 / 125
66 / 125
New, Faster HashMap/Set Implementations
Both immutable and mutable versions were completely replaced.
They substantially outperform the old implementations in most scenarios.
The mutable versions now perform on par with the Java standard
library's implementations.
67 / 125
67 / 125
Immutable scala.Seq and scala.IndexedSeq
Seq is now an alias for collection.immutable.Seq.
Before, it was an alias for the possibly-mutable collection.Seq.
IndexedSeq is now an alias for collection.immutable.IndexedSeq.
Before, it was an alias for the possibly-mutable collection.IndexedSeq.
This also changes the type of varargs in methods and pattern matches.
Arrays passed as varargs are defensively copied.
68 / 125
68 / 125
Seq is immutable in 2.13 (not in 2.12)
trait Order
trait Food
def orderFood(order: Seq[Order]): Seq[Food] = {
Seq(new Food{})
}
69 / 125
69 / 125
Seq is immutable in 2.13 (not in 2.12)
trait Order
trait Food
def orderFood(order: Seq[Order]): Seq[Food] = {
Seq(new Food{})
}
Passing a mutable ArrayBu!er ...
// We can NOT pass a mutable ArrayBuffer where an immutable Seq is expected.
val food1 = orderFood(ArrayBuffer(new Order{})) // DOES NOT COMPILE!
// [error] found : scala.collection.mutable.ArrayBuffer[Order]
// [error] required: Seq[Order]
70 / 125
70 / 125
Passing a mutable Array ...
We can pass a mutable Array where an immutable Seq is expected.
Array (unlike ArrayBuffer) is implicitly converted (and copied).
But the compiler spits out a warning.
val orderArray = Array(new Order {})
val food2 = orderFood(orderArray) // COMPILES WITH WARNING!
// [warn] Implicit conversions from Array to immutable.IndexedSeq
// [warn] are implemented by copying; Use the more efficient non-copying
// [warn] ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call.
71 / 125
71 / 125
Passing a mutable Array ...
We can pass a mutable Array where an immutable Seq is expected.
Array (unlike ArrayBuffer) is implicitly converted (and copied).
But the compiler spits out a warning.
val orderArray = Array(new Order {})
val food2 = orderFood(orderArray) // COMPILES WITH WARNING!
// [warn] Implicit conversions from Array to immutable.IndexedSeq
// [warn] are implemented by copying; Use the more efficient non-copying
// [warn] ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call.
toSeq (or toIndexedSeq) wraps the mutable Array in an immutable Seq.
val food3 = orderFood(orderArray.toSeq) // COMPILES!
val food4 = orderFood(orderArray.toIndexedSeq) // COMPILES!
72 / 125
72 / 125
Passing a immutable ArraySeq ...
ArraySeq is a new collection of Scala 2.13.
ArraySeq is an immutable array with efficient indexed access and a small
memory footprint.
mutable.ArraySeq is also available in the new collections library.
For Scala 2.12 an ArraySeq backport is provided in the scala-collection-
compat library.
ArraySeq.unsafeWrapArray wraps an Array in an ArraySeq.
val food5 = orderFood(ArraySeq(new Order{}))
val food6 = orderFood(ArraySeq.unsafeWrapArray(Array(new Order{})))
73 / 125
73 / 125
Cross-compiling Seq for 2.12 and 2.13
Seq Recap
scala.collection.Seq is a base class for scala.collection.immutable.Seq and
scala.collection.mutable.Seq in Scala 2.12 and 2.13.
scala.Seq is an alias for scala.collection.Seq in Scala 2.12.
scala.Seq is an alias for scala.collection.immutable.Seq in Scala 2.13.
74 / 125
74 / 125
Cross-compiling Seq for 2.12 and 2.13
Seq Recap
scala.collection.Seq is a base class for scala.collection.immutable.Seq and
scala.collection.mutable.Seq in Scala 2.12 and 2.13.
scala.Seq is an alias for scala.collection.Seq in Scala 2.12.
scala.Seq is an alias for scala.collection.immutable.Seq in Scala 2.13.
To make your 2.12 code cross-compilable for 2.12 and 2.13 you have
3 Options described below ...
75 / 125
75 / 125
Cross-compiling Seq (1st option)
Explicitly use scala.collection.Seq in method parameters and return types.
import scala.collection
def orderFood(order: collection.Seq[Order]): collection.Seq[Food] = ???
76 / 125
76 / 125
Cross-compiling Seq (1st option)
Explicitly use scala.collection.Seq in method parameters and return types.
import scala.collection
def orderFood(order: collection.Seq[Order]): collection.Seq[Food] = ???
You don't force your code into immutable semantics.
orderFood accepts mutable and immutable Seqs.
mutability / immutability is unspecified for the return type.
Caller must call .toSeq if she only needs an immutable result. (.toSeq only
copies elements if the result is not yet immutable.)
Simplest migration strategy!
No changes at the call site!
77 / 125
77 / 125
Cross-compiling Seq (2nd option)
Explicitly use scala.collection.Seq in parameters and
scala.collection.immutable.Seq in return types.
import scala.collection
import scala.collection.immutable
def orderFood(order: collection.Seq[Order]): immutable.Seq[Food] = ???
78 / 125
78 / 125
Cross-compiling Seq (2nd option)
Explicitly use scala.collection.Seq in parameters and
scala.collection.immutable.Seq in return types.
import scala.collection
import scala.collection.immutable
def orderFood(order: collection.Seq[Order]): immutable.Seq[Food] = ???
You force your code into immutable semantics only for return types.
orderFood accepts mutable and immutable Seqs.
immutability is fixed for the return type.
Still simple migration strategy!
Mostly no changes at the call site!
79 / 125
79 / 125
Cross-compiling Seq (3rd option)
Use scala.immutable.collection.Seq in method parameters and return types.
import scala.collection.immutable
def orderFood(order: immutable.Seq[Order]): immutable.Seq[Food] = ???
80 / 125
80 / 125
Cross-compiling Seq (3rd option)
Use scala.immutable.collection.Seq in method parameters and return types.
import scala.collection.immutable
def orderFood(order: immutable.Seq[Order]): immutable.Seq[Food] = ???
You force your code into immutable semantics.
orderFood accepts only immutable Seqs.
immutability is also fixed for the return type.
Possibly many changes at the call site to make the arguments immutable!
Use Scalafix to automate this rewrite for a large code base.
81 / 125
81 / 125
Simplified Views that Work
Views have been vastly simplified and should now work reliably.
scala.collection.View has two sub classes: scala.collection.SeqView and
scala.collection.MapView
Views are lazy. They record the operations (like filter, map etc.) and do not
execute them before invoking a terminal operation (foreach, toSeq, toMap
etc.).
82 / 125
82 / 125
Map#mapValues and Map#filterKeys
Map#mapValues and Map#filterKeys in 2.13 return MapView, not Map.
These methods are also deprecated.
Prefer using MapView#mapValues and MapView#filterKeys
83 / 125
83 / 125
Scala 2.12
val mappedValues: Map[String, Int] =
kvs.mapValues(_ + 10)
val keysFiltered: Map[Int, String] =
kvsFlipped.filterKeys(_ %2 != 0)
Scala 2.13
val mapView: MapView[String, Int] =
kvs.view.mapValues(_ + 10)
val mappedValues: Map[String, Int] =
mapView.toMap
val mapView2: MapView[Int, String] =
kvsFlipped.view.filterKeys(_ %2 != 0)
val keysFiltered: Map[Int, String] =
mapView2.toMap
Map#mapValues and Map#filterKeys
Map#mapValues and Map#filterKeys in 2.13 return MapView, not Map.
These methods are also deprecated.
Prefer using MapView#mapValues and MapView#filterKeys
val kvs = Map("one" -> 1, "two" -> 2, "three" -> 3)
def flip[A, B](t: (A, B)): (B, A) = t match { case (fst, snd) => (snd, fst) }
val kvsFlipped: Map[Int, String] = kvs.toList.map(flip).toMap
84 / 125
84 / 125
Scala 2.12
val stream: Stream[(Int, Int)] =
Stream
.continually(42)
.take(10)
.zipWithIndex
.map { case (value, index) =>
index -> value
}
Scala 2.13
val ll: LazyList[(Int, Int)] =
LazyList
.continually(42)
.take(10)
.zipWithIndex
.map { case value -> index =>
index -> value
}
LazyList replaces Stream
Stream is lazy in it's tail, but eager in it's head.
LazyList is lazy in it's head and tail.
Stream is deprecated in 2.13.
85 / 125
85 / 125
New Abtract and Concrete Collections
immutable.LazyList replaces immutable.Stream.
immutable.ArraySeq is an immutable wrapper for an array; there is also a
mutable version.
mutable.CollisionProofHashMap guards against denial-of-service attacks.
mutable.ArrayDeque is a double-ended queue that internally uses a
resizable circular buffer.
mutable.Stack was reimplemented (and undeprecated), immutable.Stack
was removed.
immutable.SeqMap (abstract) provides immutable maps which maintain
insertion order.
Implementations: VectorMap and TreeSeqMap (in addition to the already
existing ListMap)
86 / 125
86 / 125
Scala 2.12
val l1 = map.toList
val l2 = map.to[List]
import scala.collection.compat._
val l3 = map.to(List)
Scala 2.13
val l1 = map.toList
val l2 = map.to(List)
//
Coll#to converts one collection to another one.
Coll#to in 2.12 received the target type in square brackets.
Coll#to in 2.13 receives the target type's companion in parens.
The scala-collection-compat library provides the new behaviour in 2.12.
val map = Map("one" -> 1, "two" -> 2, "three" -> 3)
87 / 125
87 / 125
Added .lengthIs / .sizeIs and .sizeCompare
Allow fluent size comparisons without traversing the whole collection.
88 / 125
88 / 125
Added .lengthIs / .sizeIs and .sizeCompare
Allow fluent size comparisons without traversing the whole collection.
val xs = List.fill(5000)(scala.util.Random.nextInt)
// lenghtIs or sizeIs traverse no more than 101 element
if (xs.lengthIs > 100) {
new IllegalArgumentException("Too many elements!") pipe println
} else {
s"The list has ${xs.length} elements." pipe println
}
89 / 125
89 / 125
New .tapEach method for side-e!ects
Allows inserting side-effects in a chain of method calls on a collection or
view.
90 / 125
90 / 125
New .tapEach method for side-e!ects
Allows inserting side-effects in a chain of method calls on a collection or
view.
val doubledAndSquared =
List(1, 2, 3)
.tapEach(x => println(s"value: $x"))
.map(x => x * 2)
.tapEach(x => println(s"doubled: $x"))
.map(x => x * x)
.tapEach(x => println(s"squared: $x"))
91 / 125
91 / 125
New method List.unfold or Iterator.unfold
This allows constructing a collection or iterator from an initial element
and a repeated Option-returning operation, terminating on None.
This was added to collection companion objects and to Iterator.
92 / 125
92 / 125
New method List.unfold or Iterator.unfold
This allows constructing a collection or iterator from an initial element
and a repeated Option-returning operation, terminating on None.
This was added to collection companion objects and to Iterator.
val unfoldFunction: Int => Option[(Int, Int)] = {
case 0 => None
case s => Some(((s * s), (s - 1)))
}
List.unfold(10)(unfoldFunction) pipe println
//=> List(100, 81, 64, 49, 36, 25, 16, 9, 4, 1)
93 / 125
93 / 125
Read Lines with Iterator.unfold
def bufferedReader(fileName: String) =
new BufferedReader(new FileReader(fileName))
def readLines(reader: BufferedReader) =
Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
def readLines_dissected(reader: BufferedReader): List[String] = {
val initialState: Unit = ()
val iterator: Iterator[String] = Iterator.unfold(initialState) { _ =>
val maybeLine: Option[String] = Option(reader.readLine())
val maybeLineState: Option[(String, Unit)] = maybeLine.map(_ -> ())
maybeLineState
}
iterator.toList
}
val lines: Seq[String] =
Using.resource(bufferedReader("README.md"))(readLines)
lines foreach println
94 / 125
94 / 125
Backport to 2.12: List.unfold
def unfoldToStream[A, B](init: A)(f: A => Option[(B, A)]): Stream[B] =
f(init)
.map {
case (b, a) =>
b #:: unfoldToStream(a)(f)
}
.getOrElse(Stream.empty)
def unfoldToList[A, B](init: A)(f: A => Option[(B, A)]): List[B] =
unfoldToStream(init)(f).toList
implicit class ListCompanionOps(private val self: List.type) extends AnyVal {
@inline def unfold[A, B](init: A)(f: A => Option[(B, A)]): List[B] =
unfoldToList(init)(f)
}
95 / 125
95 / 125
Removed collection.breakOut
collection.breakOut in 2.12 inferred the return type of a collection
operation from the expected result type.
It was based on CanBuildFrom which is gone in 2.13.
To avoid constructing intermediate collections, use .view and
.to(Collection) instead.
96 / 125
96 / 125
Removed collection.breakOut
collection.breakOut in 2.12 inferred the return type of a collection
operation from the expected result type.
It was based on CanBuildFrom which is gone in 2.13.
To avoid constructing intermediate collections, use .view and
.to(Collection) instead.
val list = List(1, 2, 3) tap println
val toPair: Int => (Int, Int) = x => x -> x
// Scala 2.12 - type annotations required to infer the result type of list.map
val indexedSeq = list.map(toPair)(collection.breakOut)
val array : Array[(Int, Int)] = list.map(toPair)(collection.breakOut)
val seq : Seq[(Int, Int)] = list.map(toPair)(collection.breakOut)
val set : Set[(Int, Int)] = list.map(toPair)(collection.breakOut)
val map : Map[Int, Int] = list.map(toPair)(collection.breakOut)
// Scala 2.13 - type annotations not required
val list2 = list.view.map(toPair).to(List)
val array = list.view.map(toPair).to(Array)
val seq = list.view.map(toPair).to(Seq)
val set = list.view.map(toPair).to(Set)
97 / 125
val map = list.view.map(toPair).to(Map)
97 / 125
Two overloaded Map#map operations! Why ???
def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2]
def map[B](f: ((K, V)) => B): Iterable[B]
98 / 125
98 / 125
Two overloaded Map#map operations! Why ???
def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2]
def map[B](f: ((K, V)) => B): Iterable[B]
If the mapping function f transforms a key value pair (K, V) into another
key value pair (K2, V2), map returns a Map[K2, V2].
OTOH if the mapping function f transforms a key value pair (K, V) into
some other value B, map returns a Iterable[B].
Roughly the same holds for the two Map#flatMap operations.
To understand this we have to take a glimpse into the collections'
architecture.
99 / 125
99 / 125
7. Architecture of Collections
See also:
https://docs.scala-lang.org/overviews/core/architecture-of-scala-213-collections.html
100 / 125
100 / 125
Problem to solve
Define the return type of a collection operation in a generic way?
This is not a problem for operations, which do not return a collection, but
a single value like isEmpty, length, find, foldLeft, sum, exists, forall etc.
This is difficult for operations that return a collection like filter, take, drop,
map, flatMap, flatten etc.
101 / 125
101 / 125
Problem to solve
Define the return type of a collection operation in a generic way?
This is not a problem for operations, which do not return a collection, but
a single value like isEmpty, length, find, foldLeft, sum, exists, forall etc.
This is difficult for operations that return a collection like filter, take, drop,
map, flatMap, flatten etc.
A Non-solution with simple inheritance:
trait Iterable[A] {
def filter(f: A => Boolean): Iterable[A]
def map[B](f: A => B): Iterable[B]
}
trait List[A] extends Iterable[A] { ... }
trait Vector[A] extends Iterable[A] { ... }
The inherited methods would return an Iterable, not a List or Vector.
102 / 125
102 / 125
What we need ...
trait List[A] extends MagicBaseTrait[???, ???, ???] {
def filter(f: A => Boolean): List[A]
def map[B](f: A => B): List[B]
}
trait Vector[A] extends MagicBaseTrait[???, ???, ???] {
def filter(f: A => Boolean): Vector[A]
def map[B](f: A => B): Vector[B]
}
trait Map[K, V] extends MagicBaseTrait[???, ???, ???] {
def filter(f: (K, V) => Boolean): List[A]
def map[K2, V2](f: (K, V) => (K2, V2)): Map[K2, V2]
def map[B](f: (K, V) => B): Iterable[B]
}
... without being forced to reimplement the operations in every collection.
103 / 125
103 / 125
What we need ...
trait List[A] extends MagicBaseTrait[???, ???, ???] {
def filter(f: A => Boolean): List[A]
def map[B](f: A => B): List[B]
}
trait Vector[A] extends MagicBaseTrait[???, ???, ???] {
def filter(f: A => Boolean): Vector[A]
def map[B](f: A => B): Vector[B]
}
trait Map[K, V] extends MagicBaseTrait[???, ???, ???] {
def filter(f: (K, V) => Boolean): List[A]
def map[K2, V2](f: (K, V) => (K2, V2)): Map[K2, V2]
def map[B](f: (K, V) => B): Iterable[B]
}
... without being forced to reimplement the operations in every collection.
In the old collection implementation (up to 2.12) this problem has been
solved with CanBuildFrom.
104 / 125
104 / 125
Selections and Transformations
Selection operations (filter, take, drop etc.) do not change the elements.
The target type of the operation is exactly the same as the source type,
e.g. filter on a List[A] returns a List[A].
We must abstract over the source collection type (List[A]) in this case)
to generalize this.
Transformation operations (map, flatMap etc.) do change the elements
and in some cases (Map and some others) also the collection type.
The target type of the operation must be derived from the type
constructor of the required result collection type.
105 / 125
105 / 125
IterableOps
trait IterableOps[+A, +CC[_], +C] {
def filter(p: A => Boolean): C = ???
def map[B](f: A => B): CC[B] = ???
}
trait List[+A] extends Iterable[A] with IterableOps[A, List, List[A]] {...}
trait Vector[+A] extends Iterable[A] with IterableOps[A, Vector, Vector[A]] {...}
106 / 125
106 / 125
IterableOps
trait IterableOps[+A, +CC[_], +C] {
def filter(p: A => Boolean): C = ???
def map[B](f: A => B): CC[B] = ???
}
trait List[+A] extends Iterable[A] with IterableOps[A, List, List[A]] {...}
trait Vector[+A] extends Iterable[A] with IterableOps[A, Vector, Vector[A]] {...}
IterableOps is the MagicBaseTrait we are looking for.
IterableOps is called a template trait.
IterableOps has 3 type parameters, one for the element type (A), one for
the collection type (C) and one for the collection's type constructor type
(CC).
Leaf collection types with one type parameter (List, Vector) extend
IterableOps.
This does not work for collections like Map with two type parameters.
107 / 125
107 / 125
MapOps
trait MapOps[K, +V, +CC[_, _], +C] extends IterableOps[(K, V), Iterable, C] {
def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = ???
}
trait Map[K, V] extends Iterable[(K, V)] with MapOps[K, V, Map, Map[K, V]]
108 / 125
108 / 125
MapOps
trait MapOps[K, +V, +CC[_, _], +C] extends IterableOps[(K, V), Iterable, C] {
def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = ???
}
trait Map[K, V] extends Iterable[(K, V)] with MapOps[K, V, Map, Map[K, V]]
MapOps extends IterableOps and hence inherits all its operations.
MapOps instantiates the collection's type constructor CC with Iterable.
MapOps inherits a map operation from IterableOps returning Iterable[B].
MapOps defines another map operation overload returning Map[K2, V2].
Map inherits both map operations.
109 / 125
109 / 125
Which map is chosen ...
... when you invoke Map#map at the call site?
// from IterableOps
def map[B](f: ((K, V)) => B): Iterable[B]
// from MapOps
def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2]
map from MapOps is more specific by the rules of overloading resolution.
It will be chosen, if the map operation returns a pair of values.
Otherwise the operation from IterableOps applies.
110 / 125
110 / 125
Which map is chosen ...
... when you invoke Map#map at the call site?
// from IterableOps
def map[B](f: ((K, V)) => B): Iterable[B]
// from MapOps
def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2]
map from MapOps is more specific by the rules of overloading resolution.
It will be chosen, if the map operation returns a pair of values.
Otherwise the operation from IterableOps applies.
“same-result-type” principle:
Wherever possible a transformation method on a collection
yields a collection of the same type.
111 / 125
111 / 125
8. Migration
See also:
https://docs.scala-lang.org/overviews/core/collections-migration-213.html
112 / 125
112 / 125
When to Migrate?
when all libraryDependencies are availabe for 2.13.
when all transitive dependencies are availabe for 2.13.
113 / 125
113 / 125
Scala 2.13 Library support (2019-09-19)
Library Since Current
scalatest 3.0.8 3.0.8
scalacheck ??? 1.14.1-RC1
specs2 4.6.0 4.7.1
akka-* 2.5.23 2.5.25
akka-http 10.1.8 10.1.9
play 2.7.3 2.7.3
slick 3.3.2 3.3.2
lagom ??? 1.6.0-M5
kind-projector 0.10.3 0.10.3
shapeless 2.3.3 2.3.3
cats 2.0.0 2.0.0
cats-effect 2.0.0 2.0.0
fs2 2.0.0 2.0.0
http4s ??? 0.21.0-M4
circe 0.12.0 0.12.1
scalaz 7.2.27 7.2.28
zio ??? 1.0.0-RC12-1
apache-spark ??? 2.4.4 (only for 2.13)
114 / 125
114 / 125
Before You Migrate ...
Upgrade your project to the latest 2.12.x version.
Remove all deprecation warnings. Turn warnings into errors:
scalacOptions += -Xfatal-warnings
Scalafix them if there are many. (Turn off -Xfatal-warnings while running
scalafix. This option let's scalafix fail.)
Upgrade your sbt libraryDependencies to versions which are available in
both binary versions: 2.12 and 2.13.
115 / 125
115 / 125
Migrate an Application to 2.13
(This project must not cross compile.)
Use scalafix to automate as much as possible!
Rewrite the rest by hand.
Use new features of 2.13.
You loose backward compatibility to 2.12.
116 / 125
116 / 125
Migrate a Library to a Cross Compatible Version
(This project must cross compile.)
Keep source compatibility as much as possible without using the new
features of 2.13.
Use version specific source folders: src/main/scala-2.12 and
src/main/scala-2.13
Use scala-collection-compat library which backports many parts of the
collection API to 2.12.
Use scalafix to automate as much as possible!
Rewrite the rest by hand.
117 / 125
117 / 125
Migration Automation with Scalafix (Setup)
118 / 125
118 / 125
Migration Automation with Scalafix (Setup)
Add scalafix plugin
// project/plugins.sbt
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.7")
119 / 125
119 / 125
Migration Automation with Scalafix (Setup)
Add scalafix plugin
// project/plugins.sbt
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.7")
Change build.sbt
// build.sbt
scalafixDependencies += "org.scala-lang.modules" %% "scala-collection-migrations" % "2.1.2"
// scala-collection-compat needed only for cross compilation
libraryDependencies += "org.scala-lang.modules" %% "scala-collection-compat" % "2.1.2"
scalacOptions -= "-Xfatal-warnings" // let's scalafix fail
scalacOptions ++= List("-Yrangepos", "-P:semanticdb:synthetics:on")
120 / 125
120 / 125
Migration Automation with Scalafix (Run)
Run scalafix in sbt shell (for upgrade to 2.13)
> ;test:scalafix Collection213Upgrade ;scalafix Collection213Upgrade
Run scalafix in sbt shell (for cross compilation with 2.12 and 2.13)
> ;test:scalafix Collection213CrossCompat ;scalafix Collection213CrossCompat
Scala 2.13 Collection Compatibility Library and Migration Tool:
https://github.com/scala/scala-collection-compat
121 / 125
121 / 125
21. Resources
122 / 125
122 / 125
Resources
Code and Slides of this Talk:
https://github.com/hermannhueck/new-in-scala213
Official Release Description
https://github.com/scala/scala/releases/tag/v2.13.0
Making Our Future Better
Viktor Klang's talk at Scala Days 2019
https://www.youtube.com/watch?v=5FTJUUoT6y4
123 / 125
123 / 125
Scala 2.13 Collection Related Links
Implementing the Scala 2.13 collections
Stefan Zeiger's talk at Scala Days 2019 in Lausanne
https://www.youtube.com/watch?v=L1lxZ1LBuGI
The Architecture of Scala 2.13's Collections
https://docs.scala-lang.org/overviews/core/architecture-of-scala-213-
collections.html
Migrating a project to Scala 2.13's Collections
https://docs.scala-lang.org/overviews/core/collections-migration-213.html
Scala 2.13 Collection Compatibility Library
https://github.com/scala/scala-collection-compat
Let them be Lazy
Julien Richard Foy's blog on the lazy collection (Views and LazyList)
https://www.scala-lang.org/blog/2017/11/28/view-based-collections.html
Scala 2.13's Collections Rework
Stefan Zeiger's blog on the collections rework
https://www.scala-lang.org/blog/2017/02/28/collections-rework.html
124 / 125
124 / 125
Thank You
Q & A
https://github.com/hermannhueck/new-in-scala213
125 / 125
125 / 125

Contenu connexe

Tendances

Making The Move To Java 17 (JConf 2022)
Making The Move To Java 17 (JConf 2022)Making The Move To Java 17 (JConf 2022)
Making The Move To Java 17 (JConf 2022)Alex Motley
 
Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...
Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...
Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...Databricks
 
PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language Weaveworks
 
Thrift vs Protocol Buffers vs Avro - Biased Comparison
Thrift vs Protocol Buffers vs Avro - Biased ComparisonThrift vs Protocol Buffers vs Avro - Biased Comparison
Thrift vs Protocol Buffers vs Avro - Biased ComparisonIgor Anishchenko
 
A Deep Dive into Kafka Controller
A Deep Dive into Kafka ControllerA Deep Dive into Kafka Controller
A Deep Dive into Kafka Controllerconfluent
 
HBase and HDFS: Understanding FileSystem Usage in HBase
HBase and HDFS: Understanding FileSystem Usage in HBaseHBase and HDFS: Understanding FileSystem Usage in HBase
HBase and HDFS: Understanding FileSystem Usage in HBaseenissoz
 
Apache Iceberg - A Table Format for Hige Analytic Datasets
Apache Iceberg - A Table Format for Hige Analytic DatasetsApache Iceberg - A Table Format for Hige Analytic Datasets
Apache Iceberg - A Table Format for Hige Analytic DatasetsAlluxio, Inc.
 
Ceph Object Storage Reference Architecture Performance and Sizing Guide
Ceph Object Storage Reference Architecture Performance and Sizing GuideCeph Object Storage Reference Architecture Performance and Sizing Guide
Ceph Object Storage Reference Architecture Performance and Sizing GuideKaran Singh
 
Kafka at Peak Performance
Kafka at Peak PerformanceKafka at Peak Performance
Kafka at Peak PerformanceTodd Palino
 
Amazon S3 Best Practice and Tuning for Hadoop/Spark in the Cloud
Amazon S3 Best Practice and Tuning for Hadoop/Spark in the CloudAmazon S3 Best Practice and Tuning for Hadoop/Spark in the Cloud
Amazon S3 Best Practice and Tuning for Hadoop/Spark in the CloudNoritaka Sekiyama
 
A Day in the Life of a ClickHouse Query Webinar Slides
A Day in the Life of a ClickHouse Query Webinar Slides A Day in the Life of a ClickHouse Query Webinar Slides
A Day in the Life of a ClickHouse Query Webinar Slides Altinity Ltd
 
Cosco: An Efficient Facebook-Scale Shuffle Service
Cosco: An Efficient Facebook-Scale Shuffle ServiceCosco: An Efficient Facebook-Scale Shuffle Service
Cosco: An Efficient Facebook-Scale Shuffle ServiceDatabricks
 
Batching and Java EE (jdk.io)
Batching and Java EE (jdk.io)Batching and Java EE (jdk.io)
Batching and Java EE (jdk.io)Ryan Cuprak
 
Ceph scale testing with 10 Billion Objects
Ceph scale testing with 10 Billion ObjectsCeph scale testing with 10 Billion Objects
Ceph scale testing with 10 Billion ObjectsKaran Singh
 
Nick Fisk - low latency Ceph
Nick Fisk - low latency CephNick Fisk - low latency Ceph
Nick Fisk - low latency CephShapeBlue
 
The internals of gporca optimizer
The internals of gporca optimizerThe internals of gporca optimizer
The internals of gporca optimizerXin Zhang
 
Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0Jorge Vásquez
 

Tendances (20)

Making The Move To Java 17 (JConf 2022)
Making The Move To Java 17 (JConf 2022)Making The Move To Java 17 (JConf 2022)
Making The Move To Java 17 (JConf 2022)
 
Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...
Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...
Everyday I'm Shuffling - Tips for Writing Better Spark Programs, Strata San J...
 
PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language PromQL Deep Dive - The Prometheus Query Language
PromQL Deep Dive - The Prometheus Query Language
 
Thrift vs Protocol Buffers vs Avro - Biased Comparison
Thrift vs Protocol Buffers vs Avro - Biased ComparisonThrift vs Protocol Buffers vs Avro - Biased Comparison
Thrift vs Protocol Buffers vs Avro - Biased Comparison
 
A Deep Dive into Kafka Controller
A Deep Dive into Kafka ControllerA Deep Dive into Kafka Controller
A Deep Dive into Kafka Controller
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
HBase and HDFS: Understanding FileSystem Usage in HBase
HBase and HDFS: Understanding FileSystem Usage in HBaseHBase and HDFS: Understanding FileSystem Usage in HBase
HBase and HDFS: Understanding FileSystem Usage in HBase
 
Apache Iceberg - A Table Format for Hige Analytic Datasets
Apache Iceberg - A Table Format for Hige Analytic DatasetsApache Iceberg - A Table Format for Hige Analytic Datasets
Apache Iceberg - A Table Format for Hige Analytic Datasets
 
Ceph Object Storage Reference Architecture Performance and Sizing Guide
Ceph Object Storage Reference Architecture Performance and Sizing GuideCeph Object Storage Reference Architecture Performance and Sizing Guide
Ceph Object Storage Reference Architecture Performance and Sizing Guide
 
Kafka at Peak Performance
Kafka at Peak PerformanceKafka at Peak Performance
Kafka at Peak Performance
 
Amazon S3 Best Practice and Tuning for Hadoop/Spark in the Cloud
Amazon S3 Best Practice and Tuning for Hadoop/Spark in the CloudAmazon S3 Best Practice and Tuning for Hadoop/Spark in the Cloud
Amazon S3 Best Practice and Tuning for Hadoop/Spark in the Cloud
 
Spark
SparkSpark
Spark
 
A Day in the Life of a ClickHouse Query Webinar Slides
A Day in the Life of a ClickHouse Query Webinar Slides A Day in the Life of a ClickHouse Query Webinar Slides
A Day in the Life of a ClickHouse Query Webinar Slides
 
Cosco: An Efficient Facebook-Scale Shuffle Service
Cosco: An Efficient Facebook-Scale Shuffle ServiceCosco: An Efficient Facebook-Scale Shuffle Service
Cosco: An Efficient Facebook-Scale Shuffle Service
 
Batching and Java EE (jdk.io)
Batching and Java EE (jdk.io)Batching and Java EE (jdk.io)
Batching and Java EE (jdk.io)
 
Parquet overview
Parquet overviewParquet overview
Parquet overview
 
Ceph scale testing with 10 Billion Objects
Ceph scale testing with 10 Billion ObjectsCeph scale testing with 10 Billion Objects
Ceph scale testing with 10 Billion Objects
 
Nick Fisk - low latency Ceph
Nick Fisk - low latency CephNick Fisk - low latency Ceph
Nick Fisk - low latency Ceph
 
The internals of gporca optimizer
The internals of gporca optimizerThe internals of gporca optimizer
The internals of gporca optimizer
 
Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0Programación Funcional 101 con Scala y ZIO 2.0
Programación Funcional 101 con Scala y ZIO 2.0
 

Similaire à What's new in Scala 2.13?

Scala lens: An introduction
Scala lens: An introductionScala lens: An introduction
Scala lens: An introductionKnoldus Inc.
 
Refactoring to Scala DSLs and LiftOff 2009 Recap
Refactoring to Scala DSLs and LiftOff 2009 RecapRefactoring to Scala DSLs and LiftOff 2009 Recap
Refactoring to Scala DSLs and LiftOff 2009 RecapDave Orme
 
2014 holden - databricks umd scala crash course
2014   holden - databricks umd scala crash course2014   holden - databricks umd scala crash course
2014 holden - databricks umd scala crash courseHolden Karau
 
Functional Smalltalk
Functional SmalltalkFunctional Smalltalk
Functional SmalltalkESUG
 
5 Takeaways from Migrating a Library to Scala 3 - Scala Love
5 Takeaways from Migrating a Library to Scala 3 - Scala Love5 Takeaways from Migrating a Library to Scala 3 - Scala Love
5 Takeaways from Migrating a Library to Scala 3 - Scala LoveNatan Silnitsky
 
Real-time streaming and data pipelines with Apache Kafka
Real-time streaming and data pipelines with Apache KafkaReal-time streaming and data pipelines with Apache Kafka
Real-time streaming and data pipelines with Apache KafkaJoe Stein
 
Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3Clinton Dreisbach
 
Sbt, idea and eclipse
Sbt, idea and eclipseSbt, idea and eclipse
Sbt, idea and eclipseMike Slinn
 
Applicative Functor - Part 3
Applicative Functor - Part 3Applicative Functor - Part 3
Applicative Functor - Part 3Philip Schwarz
 
Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...
Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...
Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...Lucas Jellema
 
Artigo 81 - spark_tutorial.pdf
Artigo 81 - spark_tutorial.pdfArtigo 81 - spark_tutorial.pdf
Artigo 81 - spark_tutorial.pdfWalmirCouto3
 

Similaire à What's new in Scala 2.13? (20)

Scala lens: An introduction
Scala lens: An introductionScala lens: An introduction
Scala lens: An introduction
 
A Taste of Dotty
A Taste of DottyA Taste of Dotty
A Taste of Dotty
 
Refactoring to Scala DSLs and LiftOff 2009 Recap
Refactoring to Scala DSLs and LiftOff 2009 RecapRefactoring to Scala DSLs and LiftOff 2009 Recap
Refactoring to Scala DSLs and LiftOff 2009 Recap
 
2014 holden - databricks umd scala crash course
2014   holden - databricks umd scala crash course2014   holden - databricks umd scala crash course
2014 holden - databricks umd scala crash course
 
Intro to Rack
Intro to RackIntro to Rack
Intro to Rack
 
Functional Smalltalk
Functional SmalltalkFunctional Smalltalk
Functional Smalltalk
 
SCALA - Functional domain
SCALA -  Functional domainSCALA -  Functional domain
SCALA - Functional domain
 
5 Takeaways from Migrating a Library to Scala 3 - Scala Love
5 Takeaways from Migrating a Library to Scala 3 - Scala Love5 Takeaways from Migrating a Library to Scala 3 - Scala Love
5 Takeaways from Migrating a Library to Scala 3 - Scala Love
 
Scala active record
Scala active recordScala active record
Scala active record
 
Real-time streaming and data pipelines with Apache Kafka
Real-time streaming and data pipelines with Apache KafkaReal-time streaming and data pipelines with Apache Kafka
Real-time streaming and data pipelines with Apache Kafka
 
Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3
 
Sbt, idea and eclipse
Sbt, idea and eclipseSbt, idea and eclipse
Sbt, idea and eclipse
 
Scala+data
Scala+dataScala+data
Scala+data
 
Applicative Functor - Part 3
Applicative Functor - Part 3Applicative Functor - Part 3
Applicative Functor - Part 3
 
Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...
Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...
Introducing and Demonstrating Oracle Database 11gR2's Killer Feature – Editio...
 
Calfem34
Calfem34Calfem34
Calfem34
 
Lambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter LawreyLambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter Lawrey
 
Artigo 81 - spark_tutorial.pdf
Artigo 81 - spark_tutorial.pdfArtigo 81 - spark_tutorial.pdf
Artigo 81 - spark_tutorial.pdf
 
cheat-sheets.pdf
cheat-sheets.pdfcheat-sheets.pdf
cheat-sheets.pdf
 
Neatly folding-a-tree
Neatly folding-a-treeNeatly folding-a-tree
Neatly folding-a-tree
 

Plus de Hermann Hueck

Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in ScalaHermann Hueck
 
Future vs. Monix Task
Future vs. Monix TaskFuture vs. Monix Task
Future vs. Monix TaskHermann Hueck
 
Use Applicative where applicable!
Use Applicative where applicable!Use Applicative where applicable!
Use Applicative where applicable!Hermann Hueck
 
From Function1#apply to Kleisli
From Function1#apply to KleisliFrom Function1#apply to Kleisli
From Function1#apply to KleisliHermann Hueck
 
Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Hermann Hueck
 
From Functor Composition to Monad Transformers
From Functor Composition to Monad TransformersFrom Functor Composition to Monad Transformers
From Functor Composition to Monad TransformersHermann Hueck
 
Type Classes in Scala and Haskell
Type Classes in Scala and HaskellType Classes in Scala and Haskell
Type Classes in Scala and HaskellHermann Hueck
 
Reactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaReactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaHermann Hueck
 
Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8Hermann Hueck
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickHermann Hueck
 

Plus de Hermann Hueck (11)

Pragmatic sbt
Pragmatic sbtPragmatic sbt
Pragmatic sbt
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Future vs. Monix Task
Future vs. Monix TaskFuture vs. Monix Task
Future vs. Monix Task
 
Use Applicative where applicable!
Use Applicative where applicable!Use Applicative where applicable!
Use Applicative where applicable!
 
From Function1#apply to Kleisli
From Function1#apply to KleisliFrom Function1#apply to Kleisli
From Function1#apply to Kleisli
 
Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)
 
From Functor Composition to Monad Transformers
From Functor Composition to Monad TransformersFrom Functor Composition to Monad Transformers
From Functor Composition to Monad Transformers
 
Type Classes in Scala and Haskell
Type Classes in Scala and HaskellType Classes in Scala and Haskell
Type Classes in Scala and Haskell
 
Reactive Access to MongoDB from Scala
Reactive Access to MongoDB from ScalaReactive Access to MongoDB from Scala
Reactive Access to MongoDB from Scala
 
Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8Reactive Access to MongoDB from Java 8
Reactive Access to MongoDB from Java 8
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with Slick
 

Dernier

cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptrcbcrtm
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odishasmiwainfosol
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsSafe Software
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Angel Borroy López
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf31events.com
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentationvaddepallysandeep122
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...OnePlan Solutions
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作qr0udbr0
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfMarharyta Nedzelska
 

Dernier (20)

cpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.pptcpct NetworkING BASICS AND NETWORK TOOL.ppt
cpct NetworkING BASICS AND NETWORK TOOL.ppt
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company OdishaBalasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
Balasore Best It Company|| Top 10 IT Company || Balasore Software company Odisha
 
Powering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data StreamsPowering Real-Time Decisions with Continuous Data Streams
Powering Real-Time Decisions with Continuous Data Streams
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
Alfresco TTL#157 - Troubleshooting Made Easy: Deciphering Alfresco mTLS Confi...
 
Odoo Development Company in India | Devintelle Consulting Service
Odoo Development Company in India | Devintelle Consulting ServiceOdoo Development Company in India | Devintelle Consulting Service
Odoo Development Company in India | Devintelle Consulting Service
 
Sending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdfSending Calendar Invites on SES and Calendarsnack.pdf
Sending Calendar Invites on SES and Calendarsnack.pdf
 
PREDICTING RIVER WATER QUALITY ppt presentation
PREDICTING  RIVER  WATER QUALITY  ppt presentationPREDICTING  RIVER  WATER QUALITY  ppt presentation
PREDICTING RIVER WATER QUALITY ppt presentation
 
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
Tech Tuesday - Mastering Time Management Unlock the Power of OnePlan's Timesh...
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作英国UN学位证,北安普顿大学毕业证书1:1制作
英国UN学位证,北安普顿大学毕业证书1:1制作
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
A healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdfA healthy diet for your Java application Devoxx France.pdf
A healthy diet for your Java application Devoxx France.pdf
 
Advantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your BusinessAdvantages of Odoo ERP 17 for Your Business
Advantages of Odoo ERP 17 for Your Business
 

What's new in Scala 2.13?

  • 1. What's new in Scala 2.13? © 2019 Hermann Hueck https://github.com/hermannhueck/new-in-scala213 1 / 125
  • 2. 1 / 125 Abstract This presentation shows the feature updates from Scala 2.12 to 2.13. The list of features is not comprehensive, but it is my personal selection of favorites. I will focus on those which IMO impact/ease the programmers live most. I will look at 5 feature areas: compiler, standard library, language changes, Future and finally the most important change the redesigned collections library. I will not only show the new features of 2.13. In many cases I will show how the new features of 2.13 can be backported to 2.12 und be used in mostly the same way as in 2.13. Finally I'll give some guide lines for the migration from 2.12 to 2.13 and for a cross version project which compiles a code base with both compiler versions. 2 / 125
  • 3. 2 / 125 Agenda 1. Release Summary 2. Compiler 3. sbt Setup for Cross Compilation 4. Standard Library 5. Concurrency / Future 6. Language Changes 7. Collections 8. Architecture of Collections 9. Migration 10. Resources 3 / 125
  • 4. 3 / 125 1. Release Summary 4 / 125
  • 5. 4 / 125 Release Summary Release 2.13 improves Scala in the following areas: Collections: Standard library collections have been overhauled for simplicity, performance, and safety. This is the centerpiece of the release. 5 / 125
  • 6. 5 / 125 Release Summary Release 2.13 improves Scala in the following areas: Collections: Standard library collections have been overhauled for simplicity, performance, and safety. This is the centerpiece of the release. Future: is faster and more robust. 6 / 125
  • 7. 6 / 125 Release Summary Release 2.13 improves Scala in the following areas: Collections: Standard library collections have been overhauled for simplicity, performance, and safety. This is the centerpiece of the release. Future: is faster and more robust. Standard library: New classes have been added and new methods enhance existing classes. But also some deprecations. 7 / 125
  • 8. 7 / 125 Release Summary Release 2.13 improves Scala in the following areas: Collections: Standard library collections have been overhauled for simplicity, performance, and safety. This is the centerpiece of the release. Future: is faster and more robust. Standard library: New classes have been added and new methods enhance existing classes. But also some deprecations. Language: Literal types, partial unification is default, by-name implicits, more. 8 / 125
  • 9. 8 / 125 Release Summary Release 2.13 improves Scala in the following areas: Collections: Standard library collections have been overhauled for simplicity, performance, and safety. This is the centerpiece of the release. Future: is faster and more robust. Standard library: New classes have been added and new methods enhance existing classes. But also some deprecations. Language: Literal types, partial unification is default, by-name implicits, more. Compiler: 5-10% faster, deterministic output, improved optimizer. 9 / 125
  • 10. 9 / 125 Release Summary Release 2.13 improves Scala in the following areas: Collections: Standard library collections have been overhauled for simplicity, performance, and safety. This is the centerpiece of the release. Future: is faster and more robust. Standard library: New classes have been added and new methods enhance existing classes. But also some deprecations. Language: Literal types, partial unification is default, by-name implicits, more. Compiler: 5-10% faster, deterministic output, improved optimizer. Scala 2.13.0: https://github.com/scala/scala/releases/tag/v2.13.0 Scala 2.13.1: https://github.com/scala/scala/releases/tag/v2.13.1 10 / 125
  • 11. 10 / 125 2. Compiler 11 / 125
  • 12. 11 / 125 Compiler Performance improved Some improvments already flew back into 2.12.8, 2.12.9, 2.12.10. Deterministic, reproducible compilation Optimizer improvements (collections, arrays, inlining) 12 / 125
  • 13. 12 / 125 Macro Annotations There is no more "macro paradise" compiler plugin for 2.13. Instead, macro annotations are handled directly by the compiler. Macro annotations are enabled with the -Ymacro-annotations flag. Macro annotations remain experimental. 13 / 125
  • 14. 3. sbt Setup for Cross Compilation 14 / 125 13 / 125
  • 15. 14 / 125 Cross compile with sbt // build.sbt inThisBuild( Seq( scalaVersion := "2.13.1", crossScalaVersions := List("2.12.10", "2.13.1"), ... ) ) 15 / 125
  • 16. 15 / 125 Cross compile with sbt // build.sbt inThisBuild( Seq( scalaVersion := "2.13.1", crossScalaVersions := List("2.12.10", "2.13.1"), ... ) ) scalacOptions ++= { CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, minor)) if minor >= 13 => Seq.empty case _ => Seq( "-Ypartial-unification", // (removed in scala 2.13) allow the Compiler to // unify type constructors of different arities "-language:higherKinds", // (not required since scala 2.13.1) suppress // warnings when using higher kinded types ) } } 16 / 125
  • 17. 16 / 125 3. Standard Library 17 / 125
  • 18. 17 / 125 Smaller Footprint No longer included in the stdlib: scala-parallel-collections scala-xml scala-parser-combinators scala-swing These become libraries of their own. 18 / 125
  • 19. 18 / 125 Integrated Java Interop The old scala-java8-compat module is now part of the standard library. This provides converters for options, function types and Java streams. scala.collection.JavaConversions removed (already deprecated in 2.12). 19 / 125
  • 20. 19 / 125 Chaining: pipe and tap import scala.util.chaining._ val x: Int = 5 tap println val y: Int = 5 pipe (_ * x) tap println List(1, 2, 3) pipe (ys => println("debug: " + ys.toString)) val times6 = (_: Int) * 6 (1 - 2 - 3) .tap(v => println(s"initial: $v")) .pipe(times6) .tap(v => println(s"after times6: $v")) .pipe(scala.math.abs) .pipe(v => println(s"after scala.math.abs: $v")) 20 / 125
  • 21. 20 / 125 Chaining: pipe and tap import scala.util.chaining._ val x: Int = 5 tap println val y: Int = 5 pipe (_ * x) tap println List(1, 2, 3) pipe (ys => println("debug: " + ys.toString)) val times6 = (_: Int) * 6 (1 - 2 - 3) .tap(v => println(s"initial: $v")) .pipe(times6) .tap(v => println(s"after times6: $v")) .pipe(scala.math.abs) .pipe(v => println(s"after scala.math.abs: $v")) x pipe f is a replacement for f(x), where f is a Function1[A => B]. x tap f is a replacement for x => { f(x); x }, where f is a side-effecting Function1[A => Unit]. tap performs the side effect in f(x) and returns x unchanged. 21 / 125
  • 22. 21 / 125 Backport to 2.12: pipe and tap My 2.12 backport library uses the same package name as in 2.13 stdlib: scala.util.chaining package scala.util package object chaining { implicit class ChainingOps[A](private val a: A) { @inline def pipe[B](f: A => B): B = f(a) @inline def tap[B](f: A => Unit): A = { f(a); a } } } 22 / 125
  • 23. 22 / 125 Backport to 2.12: pipe and tap My 2.12 backport library uses the same package name as in 2.13 stdlib: scala.util.chaining package scala.util package object chaining { implicit class ChainingOps[A](private val a: A) { @inline def pipe[B](f: A => B): B = f(a) @inline def tap[B](f: A => Unit): A = { f(a); a } } } With this backport he same code runs without friction under 2.12 and 2.13. 23 / 125
  • 24. 23 / 125 Either: Right.withLeft and Left.withRight Right.apply leaves the left type unspecified. Left.apply leaves the right type unspecified. Right.withLeft and Left.withRight let you specity the unspecified type. 24 / 125
  • 25. 24 / 125 Either: Right.withLeft and Left.withRight Right.apply leaves the left type unspecified. Left.apply leaves the right type unspecified. Right.withLeft and Left.withRight let you specity the unspecified type. sbt:New in Scala 2.13> ++2.13.0 console ... Welcome to Scala 2.13.0 ... scala> Right(5) res0: scala.util.Right[Nothing,Int] = Right(5) scala> Right(5).withLeft[String] res1: scala.util.Either[String,Int] = Right(5) scala> Left("some error") res2: scala.util.Left[String,Nothing] = Left(some error) scala> Left("some error").withRight[Int] res3: scala.util.Either[String,Int] = Left(some error) 25 / 125
  • 26. 25 / 125 Backport to 2.12: Right.withLeft and Left.withRight package compat213 package object either { implicit class RightOps[L, R](private val right: Right[L, R]) { @inline def withLeft[LL >: L]: Either[LL, R] = right } implicit class LeftOps[L, R](private val left: Left[L, R]) { @inline def withRight[RR >: R]: Either[L, RR] = left } } 26 / 125
  • 27. 26 / 125 Either#flatten available since 2.13 Either#flatten is equivalent to Either#flatMap(x => x) 27 / 125
  • 28. 27 / 125 Scala 2.12 Scala 2.13 val rr = Right(Right(42)) rr.flatten //=> Right(42) val rl = Right(Left("Error RL")) rl.flatten //=> Left("Error RL") val l = Left("Error L") l.flatten //=> Left("Error L") val ll = Left(Left("Error LL")) ll.flatten //=> Left(Left("Error LL")) Either#flatten available since 2.13 Either#flatten is equivalent to Either#flatMap(x => x) val rr = Right(Right(42)) rr.flatMap(x => x) //=> Right(42) val rl = Right(Left("Error RL")) rl.flatMap(x => x) //=> Left("Error RL") val l = Left("Error L") l.flatMap(x => x) //=> Left("Error L") val ll = Left(Left("Error LL")) ll.flatMap(x => x) //=> Left(Left("Error LL")) scaladoc: Either 28 / 125
  • 29. 28 / 125 Backport to 2.12: Either#flatten package compat213 package object either { implicit class EitherOps[+L, +R](private val either: Either[L, R]) { @inline def flatten[L1 >: L, RR]( implicit ev: R <:< Either[L1, RR] ): Either[L1, RR] = either.flatMap(x => x) } } 29 / 125
  • 30. 29 / 125 String Operations: toIntOption etc. convert String literals to Int, Double, Boolean without throwing exceptions return Some(value) if the conversion succeeds, None if it fails. 30 / 125
  • 31. 30 / 125 String Operations: toIntOption etc. convert String literals to Int, Double, Boolean without throwing exceptions return Some(value) if the conversion succeeds, None if it fails. sbt:New in Scala 2.13> ++2.13.0 console ... Welcome to Scala 2.13.0 ... scala> "42".toIntOption res0: Option[Int] = Some(42) scala> "42.0".toIntOption res1: Option[Int] = None scala> "42.0".toDoubleOption res2: Option[Double] = Some(42.0) scala> "true".toBooleanOption res3: Option[Boolean] = Some(true) 31 / 125
  • 32. 31 / 125 Backport to 2.12: String Operations package compat213 package object string { implicit class StringOps(private val s: String) { import scala.util.Try @inline def toIntOption: Option[Int] = Try(s.toInt).toOption @inline def toDoubleOption: Option[Double] = Try(s.toDouble).toOption @inline def toBooleanOption: Option[Boolean] = Try(s.toBoolean).toOption } } 32 / 125
  • 33. 32 / 125 scala.util.Using.apply for resource management Similar to try ... catch ... finally, but guarantees to release/close the used resource. 33 / 125
  • 34. 33 / 125 scala.util.Using.apply for resource management Similar to try ... catch ... finally, but guarantees to release/close the used resource. def bufferedReader(fileName: String): BufferedReader = new BufferedReader(new FileReader(fileName)) def readLines(reader: BufferedReader): Seq[String] = ??? // some impl def tryLines(fileName: String): Try[Seq[String]] = Using(bufferedReader(fileName)) { reader => readLines(reader) } def catFile(fileName: String): Unit = tryLines(fileName) match { case Failure(exception) => exception.toString pipe println case Success(lines) => lines foreach println } catFile("README.md") 34 / 125
  • 35. 34 / 125 scala.util.Using.resource returns an A, not a Try[A] 35 / 125
  • 36. 35 / 125 scala.util.Using.resource returns an A, not a Try[A] package scala.util object Using { // simplified def apply[R, A](resource: => R)(f: R => A): Try[A] def resource[R, A](resource: => R)(f: R => A): A ... } 36 / 125
  • 37. 36 / 125 scala.util.Using.resource returns an A, not a Try[A] package scala.util object Using { // simplified def apply[R, A](resource: => R)(f: R => A): Try[A] def resource[R, A](resource: => R)(f: R => A): A ... } def bufferedReader(fileName: String): BufferedReader = ??? def readLines(reader: BufferedReader): Seq[String] = ??? def lines(fileName: String): Seq[String] = Using.resource(bufferedReader(fileName))(readLines) def catFile2(fileName: String): Unit = { // might throw an exception lines(fileName) foreach println } catFile2("README.md") 37 / 125
  • 38. 37 / 125 Backport to 2.12: Using package compat213 import scala.util.Try import scala.language.reflectiveCalls object Using { type Closable = { def close(): Unit } def apply[A, R <: Closable](resrc: R)(use: R => A): Try[A] = Try(resource(resrc)(use)) def resource[A, R <: Closable](resrc: R)(use: R => A): A = try { use(resrc) } finally { resrc.close() } } 38 / 125
  • 39. 38 / 125 Backport to 2.12: Using package compat213 import scala.util.Try import scala.language.reflectiveCalls object Using { type Closable = { def close(): Unit } def apply[A, R <: Closable](resrc: R)(use: R => A): Try[A] = Try(resource(resrc)(use)) def resource[A, R <: Closable](resrc: R)(use: R => A): A = try { use(resrc) } finally { resrc.close() } } This impl is a bit simplistic, but should work for resources which provide a method close. 39 / 125
  • 40. 39 / 125 s-Interpolator in Pattern matches val dateString = "11-June-2019" val s"$day-$month-$year" = dateString year pipe println month pipe println day pipe println 40 / 125
  • 41. 40 / 125 Named Product Elements Products (i.e. case classes and Tuples) now have methods productElementNames and productElementName. 41 / 125
  • 42. 41 / 125 Named Product Elements Products (i.e. case classes and Tuples) now have methods productElementNames and productElementName. sealed trait Gender extends Product with Serializable case object Male extends Gender case object Female extends Gender case class Person(name: String, age: Int, gender: Gender, email: String) { def tupled: (String, Int, Gender, String) = Person.unapply(this).get } val johndoe = Person("John Doe", 42, Male, "john@doe.com") 42 / 125
  • 43. 42 / 125 Named Product Elements Products (i.e. case classes and Tuples) now have methods productElementNames and productElementName. sealed trait Gender extends Product with Serializable case object Male extends Gender case object Female extends Gender case class Person(name: String, age: Int, gender: Gender, email: String) { def tupled: (String, Int, Gender, String) = Person.unapply(this).get } val johndoe = Person("John Doe", 42, Male, "john@doe.com") johndoe.productElementNames foreach println johndoe.productElementName(0) pipe (name => print(s"$name: ")) johndoe.productElement(0) pipe println johndoe.productElementName(1) pipe (name => print(s"$name: ")) johndoe.productElement(1) pipe println 43 / 125
  • 44. 43 / 125 Named Product Elements Naive JSON Serialization def pairToJson(name: String, value: Any): String = s"""{ "$name": $value }""" def productElementToJson(p: Product, index: Int): String = pairToJson(p.productElementName(index), p.productElement(index)) def productToJson(product: Product): String = (0 until product.productArity) .toList .map { index => productElementToJson(product, index) } .mkString("{ ", ", ", " }") implicit class ProductOps(private val product: Product) { def toJsonString: String = productToJson(product) } johndoe.toJsonString pipe println // { { "name": John Doe }, { "age": 42 }, { "gender": Male }, { "email": john@doe.com } } johndoe.tupled.toJsonString pipe println // { { "_1": John Doe }, { "_2": 42 }, { "_3": Male }, { "_4": john@doe.com } } 44 / 125
  • 45. 44 / 125 4. Concurrency / Future 45 / 125
  • 46. 45 / 125 Future + ExecutionContext Changes Overview API nearly unchanged Massive performance improvements under the hood (Future, Promise, ExecutionContext) Improved handling of failures (InterruptedException, RejectedExecutionException) Made the global ExecutionContext “batched” Added synchronous ("parasitic") ExecutionContext (releases you from writing your own synchronous ExecutionContext) For more details on the internals of the improved implementation see Viktor Klang's talk at Scala Days 2019: Making Our Future Better https://www.youtube.com/watch?v=5FTJUUoT6y4 46 / 125
  • 47. 46 / 125 Future : Minor API Changes removed onSuccess and onFailure* (already deprecated in 2.12) Future.delegate - new factory method 47 / 125
  • 48. 47 / 125 Future.delegate object Future { def apply[T](body: => T)(implicit executor: ExecutionContext): Future[T] def delegate[T](body: => Future[T])(implicit executor: ExecutionContext): Future[T] ... } 48 / 125
  • 49. 48 / 125 Future.delegate The following expressions are semantically equivalent: def expr[T]: Future[T] = ??? val f1 = Future.delegate(expr) val f2 = Future.apply(expr).flatten val f3 = Future.unit.flatMap(_ => expr) object Future { def apply[T](body: => T)(implicit executor: ExecutionContext): Future[T] def delegate[T](body: => Future[T])(implicit executor: ExecutionContext): Future[T] ... } 49 / 125
  • 50. 49 / 125 Future.delegate - Example import scala.concurrent._ import scala.concurrent.duration._ import scala.util.chaining._ implicit lazy val ec: ExecutionContext = ExecutionContext.global def plus17(x: Int): Int = x + 17 def squaredAsync(value: Int) = Future { value * value } val f1: Future[Int] = Future.apply { squaredAsync(5) }.flatten map plus17 val f2: Future[Int] = Future.unit.flatMap { _ => squaredAsync(5) } map plus17 val f3: Future[Int] = Future.delegate { squaredAsync(5) } map plus17 Await.result(f1, 3.seconds) pipe println //=> 42 Await.result(f2, 3.seconds) pipe println //=> 42 Await.result(f3, 3.seconds) pipe println //=> 42 50 / 125
  • 51. 50 / 125 5. Language Changes 51 / 125
  • 52. 51 / 125 Language Changes Overview Literal types: Literals (for strings, integers etc.) now have associated literal types. Partial unification: enabled by default By-name implicit parameters: enable implicit search to construct recursive values. Underscores in numeric literals Procedure syntax deprecated: Deprecated: def m() { ... } Use instead: def m(): Unit = { ... } View bounds deprecated: Deprecated: A <% B Use instead: (implicit ev: A => B) Symbol literals deprecated: Deprecated: 'foo Use instead: Symbol("foo") New Tuple2 arrow syntax in pattern match: 2.12 and 2.13: case (x, y) => ... 2.13 only : case x -> y => ... 52 / 125
  • 53. 52 / 125 Underscores in Number Literals val int0: Int = 1000000 val int1: Int = 1_000_000 val int2: Int = 1_0_0_0_0_0_0 // val int3: Int = 1_0_0_0_0_0_0_ // compile error: trailing separator is not allowed val long: Long = 1_000_000_000L val float: Float = 1_000.99f val double: Double = 1_000_000.999_999 53 / 125
  • 54. 53 / 125 Partial unification Partial unification is enabled by default in 2.13. The compiler no longer accepts -Ypartial-unification. The following code compiles in 2.12 only with -Ypartial-unification. 54 / 125
  • 55. 54 / 125 Partial unification Partial unification is enabled by default in 2.13. The compiler no longer accepts -Ypartial-unification. The following code compiles in 2.12 only with -Ypartial-unification. // import scala.language.higherKinds // redundant since 2.13.1 def foo[F[_], A](fa: F[A]): String = fa.toString val either: Either[String, Int] = Right(42).withLeft[String] foo { either } val intToInt: Function1[Int, Int] = x => x * 2 foo { intToInt } 55 / 125
  • 56. 55 / 125 Partial unification Partial unification is enabled by default in 2.13. The compiler no longer accepts -Ypartial-unification. The following code compiles in 2.12 only with -Ypartial-unification. // import scala.language.higherKinds // redundant since 2.13.1 def foo[F[_], A](fa: F[A]): String = fa.toString val either: Either[String, Int] = Right(42).withLeft[String] foo { either } val intToInt: Function1[Int, Int] = x => x * 2 foo { intToInt } Detailed explanation of partial unification here: https://gist.github.com/djspiewak/7a81a395c461fd3a09a6941d4cd040f2 56 / 125
  • 57. 56 / 125 scalacOptions in build.sbt for cross compilation scalacOptions ++= { CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, minor)) if minor >= 13 => Seq.empty case _ => Seq( "-Ypartial-unification", // (removed in scala 2.13) allow the Compiler to // unify type constructors of different arities "-language:higherKinds" // (not required since scala 2.13.1) suppress // warnings when using higher kinded types ) } } 57 / 125
  • 58. 57 / 125 Literal Types Literals (for strings, integers etc.) now have associated literal types. The compiler will provide instances of a new typeclass scala.ValueOf[T] for all singleton types T. The value of a singleton type can be accessed by calling method valueOf[T]. 58 / 125
  • 59. 58 / 125 Literal Types Literals (for strings, integers etc.) now have associated literal types. The compiler will provide instances of a new typeclass scala.ValueOf[T] for all singleton types T. The value of a singleton type can be accessed by calling method valueOf[T]. val wahr: true = true val foo: "foo" = "foo" val one: 1 = 1 val other_one: one.type = one implicitly[other_one.type =:= 1] val x1: Int = valueOf[42] // valueOf[42] yields an Int and is the same as ... val x2: Int = new scala.ValueOf(42).value 59 / 125
  • 60. 59 / 125 By-name Implicit Parameters were not allowed in 2.12. They enable implicit search to construct recursive values. The following code will not compile if you remove the => in (implicit rec: => Foo) . 60 / 125
  • 61. 60 / 125 By-name Implicit Parameters were not allowed in 2.12. They enable implicit search to construct recursive values. The following code will not compile if you remove the => in (implicit rec: => Foo) . trait Foo { def next: Foo } object Foo { // wouldn't compile, if rec were a call by value parameter // remove the => and try to compile ... implicit def foo(implicit rec: => Foo): Foo = new Foo { def next = rec } } val foo = implicitly[Foo] assert(foo eq foo.next) 61 / 125
  • 62. 61 / 125 6. Collections 62 / 125
  • 63. 62 / 125 Principles of the Collections Redesign simplicity better error messages easier to implement your own collection (but still complex) performance type safety, better type inference smaller footprint: parallel collections moved to a module of it's own, etc. source code compatibility - as much as possible Most ordinary code that used the old collections will continue to work as- is. But of course ... there are breaking changes. 63 / 125
  • 64. 63 / 125 Simpler Method Signatures No more CanBuildFrom Without CanBuildFrom method signatures became much simpler. 64 / 125
  • 65. 64 / 125 Simpler Method Signatures No more CanBuildFrom Without CanBuildFrom method signatures became much simpler. List#map in 2.12 List#map in 2.13 trait List[+A] extends ... { def map[B](f: A => B): List[B] = ??? } trait List[+A] extends ... { def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That = ??? } 65 / 125
  • 66. 65 / 125 Simpler Type Hierarchy No more Traversable and TraversableOnce. They remain only as deprecated aliases for Iterable and IterableOnce. Parallel collections are now a separate module. As a result, GenSeq, GenTraversableOnce, et al. are gone. 66 / 125
  • 67. 66 / 125 New, Faster HashMap/Set Implementations Both immutable and mutable versions were completely replaced. They substantially outperform the old implementations in most scenarios. The mutable versions now perform on par with the Java standard library's implementations. 67 / 125
  • 68. 67 / 125 Immutable scala.Seq and scala.IndexedSeq Seq is now an alias for collection.immutable.Seq. Before, it was an alias for the possibly-mutable collection.Seq. IndexedSeq is now an alias for collection.immutable.IndexedSeq. Before, it was an alias for the possibly-mutable collection.IndexedSeq. This also changes the type of varargs in methods and pattern matches. Arrays passed as varargs are defensively copied. 68 / 125
  • 69. 68 / 125 Seq is immutable in 2.13 (not in 2.12) trait Order trait Food def orderFood(order: Seq[Order]): Seq[Food] = { Seq(new Food{}) } 69 / 125
  • 70. 69 / 125 Seq is immutable in 2.13 (not in 2.12) trait Order trait Food def orderFood(order: Seq[Order]): Seq[Food] = { Seq(new Food{}) } Passing a mutable ArrayBu!er ... // We can NOT pass a mutable ArrayBuffer where an immutable Seq is expected. val food1 = orderFood(ArrayBuffer(new Order{})) // DOES NOT COMPILE! // [error] found : scala.collection.mutable.ArrayBuffer[Order] // [error] required: Seq[Order] 70 / 125
  • 71. 70 / 125 Passing a mutable Array ... We can pass a mutable Array where an immutable Seq is expected. Array (unlike ArrayBuffer) is implicitly converted (and copied). But the compiler spits out a warning. val orderArray = Array(new Order {}) val food2 = orderFood(orderArray) // COMPILES WITH WARNING! // [warn] Implicit conversions from Array to immutable.IndexedSeq // [warn] are implemented by copying; Use the more efficient non-copying // [warn] ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call. 71 / 125
  • 72. 71 / 125 Passing a mutable Array ... We can pass a mutable Array where an immutable Seq is expected. Array (unlike ArrayBuffer) is implicitly converted (and copied). But the compiler spits out a warning. val orderArray = Array(new Order {}) val food2 = orderFood(orderArray) // COMPILES WITH WARNING! // [warn] Implicit conversions from Array to immutable.IndexedSeq // [warn] are implemented by copying; Use the more efficient non-copying // [warn] ArraySeq.unsafeWrapArray or an explicit toIndexedSeq call. toSeq (or toIndexedSeq) wraps the mutable Array in an immutable Seq. val food3 = orderFood(orderArray.toSeq) // COMPILES! val food4 = orderFood(orderArray.toIndexedSeq) // COMPILES! 72 / 125
  • 73. 72 / 125 Passing a immutable ArraySeq ... ArraySeq is a new collection of Scala 2.13. ArraySeq is an immutable array with efficient indexed access and a small memory footprint. mutable.ArraySeq is also available in the new collections library. For Scala 2.12 an ArraySeq backport is provided in the scala-collection- compat library. ArraySeq.unsafeWrapArray wraps an Array in an ArraySeq. val food5 = orderFood(ArraySeq(new Order{})) val food6 = orderFood(ArraySeq.unsafeWrapArray(Array(new Order{}))) 73 / 125
  • 74. 73 / 125 Cross-compiling Seq for 2.12 and 2.13 Seq Recap scala.collection.Seq is a base class for scala.collection.immutable.Seq and scala.collection.mutable.Seq in Scala 2.12 and 2.13. scala.Seq is an alias for scala.collection.Seq in Scala 2.12. scala.Seq is an alias for scala.collection.immutable.Seq in Scala 2.13. 74 / 125
  • 75. 74 / 125 Cross-compiling Seq for 2.12 and 2.13 Seq Recap scala.collection.Seq is a base class for scala.collection.immutable.Seq and scala.collection.mutable.Seq in Scala 2.12 and 2.13. scala.Seq is an alias for scala.collection.Seq in Scala 2.12. scala.Seq is an alias for scala.collection.immutable.Seq in Scala 2.13. To make your 2.12 code cross-compilable for 2.12 and 2.13 you have 3 Options described below ... 75 / 125
  • 76. 75 / 125 Cross-compiling Seq (1st option) Explicitly use scala.collection.Seq in method parameters and return types. import scala.collection def orderFood(order: collection.Seq[Order]): collection.Seq[Food] = ??? 76 / 125
  • 77. 76 / 125 Cross-compiling Seq (1st option) Explicitly use scala.collection.Seq in method parameters and return types. import scala.collection def orderFood(order: collection.Seq[Order]): collection.Seq[Food] = ??? You don't force your code into immutable semantics. orderFood accepts mutable and immutable Seqs. mutability / immutability is unspecified for the return type. Caller must call .toSeq if she only needs an immutable result. (.toSeq only copies elements if the result is not yet immutable.) Simplest migration strategy! No changes at the call site! 77 / 125
  • 78. 77 / 125 Cross-compiling Seq (2nd option) Explicitly use scala.collection.Seq in parameters and scala.collection.immutable.Seq in return types. import scala.collection import scala.collection.immutable def orderFood(order: collection.Seq[Order]): immutable.Seq[Food] = ??? 78 / 125
  • 79. 78 / 125 Cross-compiling Seq (2nd option) Explicitly use scala.collection.Seq in parameters and scala.collection.immutable.Seq in return types. import scala.collection import scala.collection.immutable def orderFood(order: collection.Seq[Order]): immutable.Seq[Food] = ??? You force your code into immutable semantics only for return types. orderFood accepts mutable and immutable Seqs. immutability is fixed for the return type. Still simple migration strategy! Mostly no changes at the call site! 79 / 125
  • 80. 79 / 125 Cross-compiling Seq (3rd option) Use scala.immutable.collection.Seq in method parameters and return types. import scala.collection.immutable def orderFood(order: immutable.Seq[Order]): immutable.Seq[Food] = ??? 80 / 125
  • 81. 80 / 125 Cross-compiling Seq (3rd option) Use scala.immutable.collection.Seq in method parameters and return types. import scala.collection.immutable def orderFood(order: immutable.Seq[Order]): immutable.Seq[Food] = ??? You force your code into immutable semantics. orderFood accepts only immutable Seqs. immutability is also fixed for the return type. Possibly many changes at the call site to make the arguments immutable! Use Scalafix to automate this rewrite for a large code base. 81 / 125
  • 82. 81 / 125 Simplified Views that Work Views have been vastly simplified and should now work reliably. scala.collection.View has two sub classes: scala.collection.SeqView and scala.collection.MapView Views are lazy. They record the operations (like filter, map etc.) and do not execute them before invoking a terminal operation (foreach, toSeq, toMap etc.). 82 / 125
  • 83. 82 / 125 Map#mapValues and Map#filterKeys Map#mapValues and Map#filterKeys in 2.13 return MapView, not Map. These methods are also deprecated. Prefer using MapView#mapValues and MapView#filterKeys 83 / 125
  • 84. 83 / 125 Scala 2.12 val mappedValues: Map[String, Int] = kvs.mapValues(_ + 10) val keysFiltered: Map[Int, String] = kvsFlipped.filterKeys(_ %2 != 0) Scala 2.13 val mapView: MapView[String, Int] = kvs.view.mapValues(_ + 10) val mappedValues: Map[String, Int] = mapView.toMap val mapView2: MapView[Int, String] = kvsFlipped.view.filterKeys(_ %2 != 0) val keysFiltered: Map[Int, String] = mapView2.toMap Map#mapValues and Map#filterKeys Map#mapValues and Map#filterKeys in 2.13 return MapView, not Map. These methods are also deprecated. Prefer using MapView#mapValues and MapView#filterKeys val kvs = Map("one" -> 1, "two" -> 2, "three" -> 3) def flip[A, B](t: (A, B)): (B, A) = t match { case (fst, snd) => (snd, fst) } val kvsFlipped: Map[Int, String] = kvs.toList.map(flip).toMap 84 / 125
  • 85. 84 / 125 Scala 2.12 val stream: Stream[(Int, Int)] = Stream .continually(42) .take(10) .zipWithIndex .map { case (value, index) => index -> value } Scala 2.13 val ll: LazyList[(Int, Int)] = LazyList .continually(42) .take(10) .zipWithIndex .map { case value -> index => index -> value } LazyList replaces Stream Stream is lazy in it's tail, but eager in it's head. LazyList is lazy in it's head and tail. Stream is deprecated in 2.13. 85 / 125
  • 86. 85 / 125 New Abtract and Concrete Collections immutable.LazyList replaces immutable.Stream. immutable.ArraySeq is an immutable wrapper for an array; there is also a mutable version. mutable.CollisionProofHashMap guards against denial-of-service attacks. mutable.ArrayDeque is a double-ended queue that internally uses a resizable circular buffer. mutable.Stack was reimplemented (and undeprecated), immutable.Stack was removed. immutable.SeqMap (abstract) provides immutable maps which maintain insertion order. Implementations: VectorMap and TreeSeqMap (in addition to the already existing ListMap) 86 / 125
  • 87. 86 / 125 Scala 2.12 val l1 = map.toList val l2 = map.to[List] import scala.collection.compat._ val l3 = map.to(List) Scala 2.13 val l1 = map.toList val l2 = map.to(List) // Coll#to converts one collection to another one. Coll#to in 2.12 received the target type in square brackets. Coll#to in 2.13 receives the target type's companion in parens. The scala-collection-compat library provides the new behaviour in 2.12. val map = Map("one" -> 1, "two" -> 2, "three" -> 3) 87 / 125
  • 88. 87 / 125 Added .lengthIs / .sizeIs and .sizeCompare Allow fluent size comparisons without traversing the whole collection. 88 / 125
  • 89. 88 / 125 Added .lengthIs / .sizeIs and .sizeCompare Allow fluent size comparisons without traversing the whole collection. val xs = List.fill(5000)(scala.util.Random.nextInt) // lenghtIs or sizeIs traverse no more than 101 element if (xs.lengthIs > 100) { new IllegalArgumentException("Too many elements!") pipe println } else { s"The list has ${xs.length} elements." pipe println } 89 / 125
  • 90. 89 / 125 New .tapEach method for side-e!ects Allows inserting side-effects in a chain of method calls on a collection or view. 90 / 125
  • 91. 90 / 125 New .tapEach method for side-e!ects Allows inserting side-effects in a chain of method calls on a collection or view. val doubledAndSquared = List(1, 2, 3) .tapEach(x => println(s"value: $x")) .map(x => x * 2) .tapEach(x => println(s"doubled: $x")) .map(x => x * x) .tapEach(x => println(s"squared: $x")) 91 / 125
  • 92. 91 / 125 New method List.unfold or Iterator.unfold This allows constructing a collection or iterator from an initial element and a repeated Option-returning operation, terminating on None. This was added to collection companion objects and to Iterator. 92 / 125
  • 93. 92 / 125 New method List.unfold or Iterator.unfold This allows constructing a collection or iterator from an initial element and a repeated Option-returning operation, terminating on None. This was added to collection companion objects and to Iterator. val unfoldFunction: Int => Option[(Int, Int)] = { case 0 => None case s => Some(((s * s), (s - 1))) } List.unfold(10)(unfoldFunction) pipe println //=> List(100, 81, 64, 49, 36, 25, 16, 9, 4, 1) 93 / 125
  • 94. 93 / 125 Read Lines with Iterator.unfold def bufferedReader(fileName: String) = new BufferedReader(new FileReader(fileName)) def readLines(reader: BufferedReader) = Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList def readLines_dissected(reader: BufferedReader): List[String] = { val initialState: Unit = () val iterator: Iterator[String] = Iterator.unfold(initialState) { _ => val maybeLine: Option[String] = Option(reader.readLine()) val maybeLineState: Option[(String, Unit)] = maybeLine.map(_ -> ()) maybeLineState } iterator.toList } val lines: Seq[String] = Using.resource(bufferedReader("README.md"))(readLines) lines foreach println 94 / 125
  • 95. 94 / 125 Backport to 2.12: List.unfold def unfoldToStream[A, B](init: A)(f: A => Option[(B, A)]): Stream[B] = f(init) .map { case (b, a) => b #:: unfoldToStream(a)(f) } .getOrElse(Stream.empty) def unfoldToList[A, B](init: A)(f: A => Option[(B, A)]): List[B] = unfoldToStream(init)(f).toList implicit class ListCompanionOps(private val self: List.type) extends AnyVal { @inline def unfold[A, B](init: A)(f: A => Option[(B, A)]): List[B] = unfoldToList(init)(f) } 95 / 125
  • 96. 95 / 125 Removed collection.breakOut collection.breakOut in 2.12 inferred the return type of a collection operation from the expected result type. It was based on CanBuildFrom which is gone in 2.13. To avoid constructing intermediate collections, use .view and .to(Collection) instead. 96 / 125
  • 97. 96 / 125 Removed collection.breakOut collection.breakOut in 2.12 inferred the return type of a collection operation from the expected result type. It was based on CanBuildFrom which is gone in 2.13. To avoid constructing intermediate collections, use .view and .to(Collection) instead. val list = List(1, 2, 3) tap println val toPair: Int => (Int, Int) = x => x -> x // Scala 2.12 - type annotations required to infer the result type of list.map val indexedSeq = list.map(toPair)(collection.breakOut) val array : Array[(Int, Int)] = list.map(toPair)(collection.breakOut) val seq : Seq[(Int, Int)] = list.map(toPair)(collection.breakOut) val set : Set[(Int, Int)] = list.map(toPair)(collection.breakOut) val map : Map[Int, Int] = list.map(toPair)(collection.breakOut) // Scala 2.13 - type annotations not required val list2 = list.view.map(toPair).to(List) val array = list.view.map(toPair).to(Array) val seq = list.view.map(toPair).to(Seq) val set = list.view.map(toPair).to(Set) 97 / 125
  • 98. val map = list.view.map(toPair).to(Map) 97 / 125 Two overloaded Map#map operations! Why ??? def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2] def map[B](f: ((K, V)) => B): Iterable[B] 98 / 125
  • 99. 98 / 125 Two overloaded Map#map operations! Why ??? def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2] def map[B](f: ((K, V)) => B): Iterable[B] If the mapping function f transforms a key value pair (K, V) into another key value pair (K2, V2), map returns a Map[K2, V2]. OTOH if the mapping function f transforms a key value pair (K, V) into some other value B, map returns a Iterable[B]. Roughly the same holds for the two Map#flatMap operations. To understand this we have to take a glimpse into the collections' architecture. 99 / 125
  • 100. 99 / 125 7. Architecture of Collections See also: https://docs.scala-lang.org/overviews/core/architecture-of-scala-213-collections.html 100 / 125
  • 101. 100 / 125 Problem to solve Define the return type of a collection operation in a generic way? This is not a problem for operations, which do not return a collection, but a single value like isEmpty, length, find, foldLeft, sum, exists, forall etc. This is difficult for operations that return a collection like filter, take, drop, map, flatMap, flatten etc. 101 / 125
  • 102. 101 / 125 Problem to solve Define the return type of a collection operation in a generic way? This is not a problem for operations, which do not return a collection, but a single value like isEmpty, length, find, foldLeft, sum, exists, forall etc. This is difficult for operations that return a collection like filter, take, drop, map, flatMap, flatten etc. A Non-solution with simple inheritance: trait Iterable[A] { def filter(f: A => Boolean): Iterable[A] def map[B](f: A => B): Iterable[B] } trait List[A] extends Iterable[A] { ... } trait Vector[A] extends Iterable[A] { ... } The inherited methods would return an Iterable, not a List or Vector. 102 / 125
  • 103. 102 / 125 What we need ... trait List[A] extends MagicBaseTrait[???, ???, ???] { def filter(f: A => Boolean): List[A] def map[B](f: A => B): List[B] } trait Vector[A] extends MagicBaseTrait[???, ???, ???] { def filter(f: A => Boolean): Vector[A] def map[B](f: A => B): Vector[B] } trait Map[K, V] extends MagicBaseTrait[???, ???, ???] { def filter(f: (K, V) => Boolean): List[A] def map[K2, V2](f: (K, V) => (K2, V2)): Map[K2, V2] def map[B](f: (K, V) => B): Iterable[B] } ... without being forced to reimplement the operations in every collection. 103 / 125
  • 104. 103 / 125 What we need ... trait List[A] extends MagicBaseTrait[???, ???, ???] { def filter(f: A => Boolean): List[A] def map[B](f: A => B): List[B] } trait Vector[A] extends MagicBaseTrait[???, ???, ???] { def filter(f: A => Boolean): Vector[A] def map[B](f: A => B): Vector[B] } trait Map[K, V] extends MagicBaseTrait[???, ???, ???] { def filter(f: (K, V) => Boolean): List[A] def map[K2, V2](f: (K, V) => (K2, V2)): Map[K2, V2] def map[B](f: (K, V) => B): Iterable[B] } ... without being forced to reimplement the operations in every collection. In the old collection implementation (up to 2.12) this problem has been solved with CanBuildFrom. 104 / 125
  • 105. 104 / 125 Selections and Transformations Selection operations (filter, take, drop etc.) do not change the elements. The target type of the operation is exactly the same as the source type, e.g. filter on a List[A] returns a List[A]. We must abstract over the source collection type (List[A]) in this case) to generalize this. Transformation operations (map, flatMap etc.) do change the elements and in some cases (Map and some others) also the collection type. The target type of the operation must be derived from the type constructor of the required result collection type. 105 / 125
  • 106. 105 / 125 IterableOps trait IterableOps[+A, +CC[_], +C] { def filter(p: A => Boolean): C = ??? def map[B](f: A => B): CC[B] = ??? } trait List[+A] extends Iterable[A] with IterableOps[A, List, List[A]] {...} trait Vector[+A] extends Iterable[A] with IterableOps[A, Vector, Vector[A]] {...} 106 / 125
  • 107. 106 / 125 IterableOps trait IterableOps[+A, +CC[_], +C] { def filter(p: A => Boolean): C = ??? def map[B](f: A => B): CC[B] = ??? } trait List[+A] extends Iterable[A] with IterableOps[A, List, List[A]] {...} trait Vector[+A] extends Iterable[A] with IterableOps[A, Vector, Vector[A]] {...} IterableOps is the MagicBaseTrait we are looking for. IterableOps is called a template trait. IterableOps has 3 type parameters, one for the element type (A), one for the collection type (C) and one for the collection's type constructor type (CC). Leaf collection types with one type parameter (List, Vector) extend IterableOps. This does not work for collections like Map with two type parameters. 107 / 125
  • 108. 107 / 125 MapOps trait MapOps[K, +V, +CC[_, _], +C] extends IterableOps[(K, V), Iterable, C] { def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = ??? } trait Map[K, V] extends Iterable[(K, V)] with MapOps[K, V, Map, Map[K, V]] 108 / 125
  • 109. 108 / 125 MapOps trait MapOps[K, +V, +CC[_, _], +C] extends IterableOps[(K, V), Iterable, C] { def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = ??? } trait Map[K, V] extends Iterable[(K, V)] with MapOps[K, V, Map, Map[K, V]] MapOps extends IterableOps and hence inherits all its operations. MapOps instantiates the collection's type constructor CC with Iterable. MapOps inherits a map operation from IterableOps returning Iterable[B]. MapOps defines another map operation overload returning Map[K2, V2]. Map inherits both map operations. 109 / 125
  • 110. 109 / 125 Which map is chosen ... ... when you invoke Map#map at the call site? // from IterableOps def map[B](f: ((K, V)) => B): Iterable[B] // from MapOps def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2] map from MapOps is more specific by the rules of overloading resolution. It will be chosen, if the map operation returns a pair of values. Otherwise the operation from IterableOps applies. 110 / 125
  • 111. 110 / 125 Which map is chosen ... ... when you invoke Map#map at the call site? // from IterableOps def map[B](f: ((K, V)) => B): Iterable[B] // from MapOps def map[K2, V2](f: ((K, V)) => (K2, V2)): Map[K2, V2] map from MapOps is more specific by the rules of overloading resolution. It will be chosen, if the map operation returns a pair of values. Otherwise the operation from IterableOps applies. “same-result-type” principle: Wherever possible a transformation method on a collection yields a collection of the same type. 111 / 125
  • 112. 111 / 125 8. Migration See also: https://docs.scala-lang.org/overviews/core/collections-migration-213.html 112 / 125
  • 113. 112 / 125 When to Migrate? when all libraryDependencies are availabe for 2.13. when all transitive dependencies are availabe for 2.13. 113 / 125
  • 114. 113 / 125 Scala 2.13 Library support (2019-09-19) Library Since Current scalatest 3.0.8 3.0.8 scalacheck ??? 1.14.1-RC1 specs2 4.6.0 4.7.1 akka-* 2.5.23 2.5.25 akka-http 10.1.8 10.1.9 play 2.7.3 2.7.3 slick 3.3.2 3.3.2 lagom ??? 1.6.0-M5 kind-projector 0.10.3 0.10.3 shapeless 2.3.3 2.3.3 cats 2.0.0 2.0.0 cats-effect 2.0.0 2.0.0 fs2 2.0.0 2.0.0 http4s ??? 0.21.0-M4 circe 0.12.0 0.12.1 scalaz 7.2.27 7.2.28 zio ??? 1.0.0-RC12-1 apache-spark ??? 2.4.4 (only for 2.13) 114 / 125
  • 115. 114 / 125 Before You Migrate ... Upgrade your project to the latest 2.12.x version. Remove all deprecation warnings. Turn warnings into errors: scalacOptions += -Xfatal-warnings Scalafix them if there are many. (Turn off -Xfatal-warnings while running scalafix. This option let's scalafix fail.) Upgrade your sbt libraryDependencies to versions which are available in both binary versions: 2.12 and 2.13. 115 / 125
  • 116. 115 / 125 Migrate an Application to 2.13 (This project must not cross compile.) Use scalafix to automate as much as possible! Rewrite the rest by hand. Use new features of 2.13. You loose backward compatibility to 2.12. 116 / 125
  • 117. 116 / 125 Migrate a Library to a Cross Compatible Version (This project must cross compile.) Keep source compatibility as much as possible without using the new features of 2.13. Use version specific source folders: src/main/scala-2.12 and src/main/scala-2.13 Use scala-collection-compat library which backports many parts of the collection API to 2.12. Use scalafix to automate as much as possible! Rewrite the rest by hand. 117 / 125
  • 118. 117 / 125 Migration Automation with Scalafix (Setup) 118 / 125
  • 119. 118 / 125 Migration Automation with Scalafix (Setup) Add scalafix plugin // project/plugins.sbt addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.7") 119 / 125
  • 120. 119 / 125 Migration Automation with Scalafix (Setup) Add scalafix plugin // project/plugins.sbt addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.7") Change build.sbt // build.sbt scalafixDependencies += "org.scala-lang.modules" %% "scala-collection-migrations" % "2.1.2" // scala-collection-compat needed only for cross compilation libraryDependencies += "org.scala-lang.modules" %% "scala-collection-compat" % "2.1.2" scalacOptions -= "-Xfatal-warnings" // let's scalafix fail scalacOptions ++= List("-Yrangepos", "-P:semanticdb:synthetics:on") 120 / 125
  • 121. 120 / 125 Migration Automation with Scalafix (Run) Run scalafix in sbt shell (for upgrade to 2.13) > ;test:scalafix Collection213Upgrade ;scalafix Collection213Upgrade Run scalafix in sbt shell (for cross compilation with 2.12 and 2.13) > ;test:scalafix Collection213CrossCompat ;scalafix Collection213CrossCompat Scala 2.13 Collection Compatibility Library and Migration Tool: https://github.com/scala/scala-collection-compat 121 / 125
  • 122. 121 / 125 21. Resources 122 / 125
  • 123. 122 / 125 Resources Code and Slides of this Talk: https://github.com/hermannhueck/new-in-scala213 Official Release Description https://github.com/scala/scala/releases/tag/v2.13.0 Making Our Future Better Viktor Klang's talk at Scala Days 2019 https://www.youtube.com/watch?v=5FTJUUoT6y4 123 / 125
  • 124. 123 / 125 Scala 2.13 Collection Related Links Implementing the Scala 2.13 collections Stefan Zeiger's talk at Scala Days 2019 in Lausanne https://www.youtube.com/watch?v=L1lxZ1LBuGI The Architecture of Scala 2.13's Collections https://docs.scala-lang.org/overviews/core/architecture-of-scala-213- collections.html Migrating a project to Scala 2.13's Collections https://docs.scala-lang.org/overviews/core/collections-migration-213.html Scala 2.13 Collection Compatibility Library https://github.com/scala/scala-collection-compat Let them be Lazy Julien Richard Foy's blog on the lazy collection (Views and LazyList) https://www.scala-lang.org/blog/2017/11/28/view-based-collections.html Scala 2.13's Collections Rework Stefan Zeiger's blog on the collections rework https://www.scala-lang.org/blog/2017/02/28/collections-rework.html 124 / 125
  • 125. 124 / 125 Thank You Q & A https://github.com/hermannhueck/new-in-scala213 125 / 125