Migrating to Scala 2.13
Julien Richard-Foy, Scala Center
Stefan Zeiger, Lightbend

Scala 2.13

Scala 2.13 Roadmap• Simplifying the collections• Compiler performance• Modularizing the standard library• User-friendliness
https://github.com/scala/scala-dev/issues/324

Current Status• Scala 2.13.0-M4 to be released shortly• First milestone with the new collections library• Final milestone release: M5• Minor API changes, bug fixes and performance improvements

Starting Point• Project that works on Scala 2.12
• Targeting Scala 2.13.0-M4 or higher• sbt build (for examples shown)Prepare for migration

Clean up on 2.12: Deprecations• Remove deprecated calls• Many deprecated APIs were removed in 2.13 (e.g. JavaConversions)• A clean build without deprecations on 2.12 makes the migration easier• build.sbt:
scalacOptions in Compile += "-deprecation"

Clean up on 2.12: Compiler flags• Compiler flags that modify the language are being removed in 2.13
• Only -Xsource from now on• Compile without the following flags:• -Yno-adapted-args• -Xstrict-inference• -Xfull-lubs• -Yoverride-objects• -Yoverride-vars• -Yinfer-argument-types• -Yvirtpatmat• See https://github.com/scala/scala/pull/6505

Clean up on 2.12: Argument adaptation / Auto-tupling• Argument adaptation cannot be turned off selectively any more
• -Yno-adapted-args removed• Dotty has more restrictive adaptation• Scala 2.14 will align with Dotty rules• Automatic eta-expansion of zero-arg methods already disabled in 2.13
under -Xsource:2.14 (see https://github.com/scala/scala/pull/6475)

Clean up on 2.12: Modules• scala-library-all has been removed• Depend on the required modules individually• Parallel collections have been moved into a separate module• Add a dependency to scala-parallel-collections on 2.13 and see
https://github.com/scala/scala-parallel-collections/issues/22 for crossbuilding• scala-xml is no longer a transitive dependency of scala-compiler• Depend on it directly if necessary

Build on 2.13

Cross-building: Basic setup
crossScalaVersions := Seq("2.13.0-M4-pre-20d3c21", "2.12.6")
scalaVersion := crossScalaVersions.value.head

Cross-building: Compiler options
scalacOptions ++=(CrossVersion.partialVersion(scalaVersion.value) match {case Some((2, n)) if n >= 13 => Seq("-Xsource:2.14")
case _=> Seq("-Yno-adapted-args")})

Build on 2.13
• Expect some breakage due to the new collections library• Singleton types and minor type inference changes can influence types andimplicit resolution in corner cases• Other small changes in source compatibility (see https://github.com/scala/scaladev/issues/470)• Try -Xsource:2.12

scala-collection-compat• The new collections library is mostly source compatible• Many old methods and types are deprecated• e.g. Traversable, TraversableOnce, Stream, ...
• Some features could not be added back in a 2.12-compatible way• Instead scala-collection-compat provides the 2.13 syntax on 2.11 & 2.12
• https://github.com/scala/scala-collection-compat/

scala-collection-compat: Example• build.sbt:
libraryDependencies +="org.scala-lang" %% "scala-collection-compat" % "0.1"• Old Scala code:
xs.to[List]• New Scala code:
import scala.collection.compat._
xs.to(List)

Not yetavailable

New collections

Goals of the collections redesign• This is not the topic of this talk, but in short:• Simpler user-facing API (no CanBuildFrom)• Correct operation implementations for non-strict collections• Simpler internal hierarchy (no Gen… types)

Major collection incompatibilities: Immutable Seq• scala.Seq is now scala.collection.immutable.Seq• Same for IndexedSeq• Consistent with Set and Map• Decide on a case-by-case basis which one to use• Use s.c.Seq or s.c.i.Seq explicitly when cross-buildingMajor collection incompatibilities: Immutable Seq• Varargs use scala.Seq, so also immutable now• Wrapped Java varargs pretend the array is immutable• Using the new s.c.i.ArraySeq• You can do the same with ArraySeq.unsafeWrapArray• A deprecated implicit conversion makes a copy of the array
method copyArrayToImmutableIndexedSeq in class LowPriorityImplicits2 is deprecated(since 2.13.0): Implicit conversions from Array to immutable.IndexedSeq are implemented
by copying; Use the more efficient non-copying ArraySeq.unsafeWrapArray or an
explicit toIndexedSeq call

Major collection incompatibilities: CanBuildFrom• CanBuildFrom replaced by BuildFrom• BuildFrom requires an instance of the source collection:trait BuildFrom[-From, -A, +C] {def fromSpecificIterable(from: From)(it: Iterable[A]): C
def newBuilder(from: From): Builder[A, C]}• Used in methods like Future.sequence• Collection methods like flatMap no longer need it• Overloaded to produce different results depending on source collection type

Major collection incompatibilities: CanBuildFrom• Factory allows target type-driven building (like CanBuild in 2.12)• Used in Iterable.to• Like BuildFrom but does not use a source collection:trait Factory[-A, +C] {def fromSpecific(it: IterableOnce[A]): C
def newBuilder: Builder[A, C]}• General rule: Use BuildFrom to rebuild with the best matching type of an
existing source collection, otherwise use FactoryMajor collection incompatibilities: breakOut• Without CanBuildFrom, there is no breakOut• Use an Iterator: xs.iterator.map(…).to(Vector)• Methods with BuildFrom or Factory can be called with a companion object
instead (via implicit conversion to BuildFrom / Factory):• Old:

Doesn't actually work in 2.12val xs: List[Future[Int]] = …
Future.sequence(xs)(breakOut, implicitly):
Future[Vector[Int]]• New:
val xs: List[Future[Int]] = …
Future.sequence(xs)(Vector, implicitly)Major collection incompatibilities: Views• Views in the new collections are reified Iterator operations• View vs Iterator is similar to Stream vs Spliterator in Java 8collections• Views don't remember source collection types• Use an explicit to… operation instead of force to build the desired type• mapValues and filterKeys on Map now return MapView• It was always a lazy View-like object but pretended to be a Map• Add .toMap if necessaryMajor collection incompatibilities: Custom collections• Only very simple collection implementations are source compatible• General rule: Use two different source files in different source directories tocross-build• Type hierarchy simplified• Less boilerplate• All methods have alphabetic names, symbolic operators are aliases• E.g. override concat instead of ++• Overloading instead of CanBuildFrom

Cross-building: Source directories
crossScalaVersions := Seq("2.13.0-M4-pre-20d3c21", "2.12.6")
scalaVersion := crossScalaVersions.value.head// Add src/main/scala-2.13+ for Scala 2.13 and newer// and src/main/scala-2.12- for Scala versions older than 2.13
unmanagedSourceDirectories in Compile += {val sourceDir = (sourceDirectory in Compile).value
CrossVersion.partialVersion(scalaVersion.value) match {case Some((2, n)) if n >= 13 => sourceDir / "scala-2.13+"
case _=> sourceDir / "scala-2.12-"}}sbt already gives you:• scala• scala-2.12• scala-2.13.0-M4-pre-20d3c21

Major collection incompatibilities• See https://github.com/scala/collection-strawman/wiki/FAQ for a morecomprehensive list

Automated migration

Migrating an application

2.12

Rules under development –more in M5

Scalafixmanual work

2.13

Running the migration rules• plugins.sbt:

Step 1: Add sbt-scalafix toyour project

addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.7")• From your shell:

Step 2: Run the migration$ sbtrule> scalafixEnable> scalafix github:scala/scala-collectioncompat/NewCollections• build.sbt:
scalaVersion := "2.13.0"

Step 3: Update thescalaVersion• And then:> ;reload ;compile• Documentation: https://github.com/scala/scala-collection-compat

Scope of the migration rules• Renamings

Has a lazy head

• Stream(1, 2, 3).append(Stream(4, 5, 6))• LazyList(1, 2, 3).lazyAppendedAll(LazyList(4, 5, 6))• Expression rewritings•

xs.copyToBuffer(b)•b ++= xs

• The more complex the expression, the harder to implement the rewrite rule

Not in the scope of the migration• Custom collection implementations• Advanced usage of CanBuildFrom• scala.Seq usage• Comprehensive list of supported rewrites:
https://github.com/scala/collection-strawman/wiki/FAQ

Migrating a library
2.11
2.12

Rules not yet implemented

Scalafix

2.11
2.12
2.13

manual work

scala-collection-compat

Links• Draft of "The Architecture of Scala Collections" for 2.13:
https://github.com/scala/docs.scala-lang/pull/960• Demo project for cross-building:https://github.com/szeiger/new-collections-demo• FAQ: https://github.com/scala/collection-strawman/wiki/FAQ• Previous talks on the new collections:• The new collections library for Scala 2.13 and Dotty• The next() collections•@julienrf•@StefanZeiger