Skip to content

Add guidance on how to migrate scala.Seq #1326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 21, 2019
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions _overviews/core/collections-migration-213.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ how to cross-build projects with Scala 2.11 / 2.12 and 2.13.
For an in-depth overview of the Scala 2.13 collections library, see the [collections guide]({{ site.baseurl }}/overviews/collections-2.13/introduction.html). The implementation details of the 2.13 collections are explained in the document [the architecture of Scala collections]({{ site.baseurl }}/overviews/core/architecture-of-scala-213-collections.html).

The most important changes in the Scala 2.13 collections library are:
- `scala.Seq[+A]` is now an alias for `scala.collection.immutable.Seq[A]` (instead of `scala.collection.Seq[A]`). Note that this also changes the type of Scala varargs methods.
- Transformation methods no longer have an implicit `CanBuildFrom` parameter. This makes the library easier to understand (in source code, Scaladoc, and IDE code completion). It also makes compiling user code more efficient.
- The type hierarchy is simplified. `Traversable` no longer exists, only `Iterable`.
- The `to[Collection]` method was replaced by the `to(Collection)` method.
Expand All @@ -22,14 +23,38 @@ The most important changes in the Scala 2.13 collections library are:
- Deprecated collections were removed (`MutableList`, `immutable.Stack`, others)
- Parallel collections are now in a separate hierarchy in a [separate module](https://github.com/scala/scala-parallel-collections).
- The `scala.jdk.StreamConverters` object provides extension methods to create (sequential or parallel) Java 8 streams for Scala collections.
- `scala.Seq` is now an alias for `scala.collection.immutable.Seq` (no longer `scala.collection.Seq`). Note that this also changes the type of Scala varargs methods.

## Tools for migrating and cross-building

The [scala-collection-compat](https://github.com/scala/scala-collection-compat) is a library released for 2.11, 2.12 and 2.13 that provides some of the new APIs from Scala 2.13 for the older versions. This simplifies cross-building projects.

The module also provides [migratrion rules](https://github.com/scala/scala-collection-compat#migration-tool) for [scalafix](https://scalacenter.github.io/scalafix/docs/users/installation.html) that can update a project's source code to work with the 2.13 collections library.

## scala.Seq migration

In Scala 2.13 `scala.Seq[+A]` is an alias for `scala.collection.immutable.Seq[A]`, instead of `scala.collection.Seq[A]`. This change requires some planning depending on how your code is going to be used.

If you're making an application, and simply migrating a Scala 2.12 code base to 2.13, it might be ok to keep using `scala.Seq` in your code.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like "keep using scala.Seq" should be the safe and simple option. I'd argue it's the opposite (unless your code happens to just work out of the box). In a complex codebase the simplest way forward is to change all references from scala.Seq to scala.collection.Seq and possibly insert toSeq calls where necessary. This is easy to do and will cross-compile on 2.12 without any changes in semantics.

Switching your own codebase from scala.collection.Seq to scala.immutable.Seq is the more advanced refactoring. In many cases it may have been the intention all along, so it makes sense to do it. It also avoids unnecessary copying when varargs are involved, but it's much harder to get right consistently in a large codebase.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. I'll mention recommendation to use scala.collection.Seq first then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this in 2b4de2d.


If you're making a library intended to be used by other programmers, then using `scala.Seq` or varargs is going to be a breaking change in the API semantics. For example, if there was a function `def orderFood(order: Seq[Order]): Seq[Food]`, previously the library user would have been able to pass in an array of `Order`, but it won't work for 2.13.

- if you cross build with Scala 2.12 and want to maintain the API semantics for 2.13 version of your library, or
- if your library users frequently uses mutable collections such as `Array`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If code has been optimized to the point that you build an array instead of a regular collection, you're probably okay calling unsafeWrapArray, so it will still work with immutable varargs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrays might come up simply as Java API that you provide as a subset of API.


you can import `scala.collection.Seq` ("CSeq") explicitly in your code.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth mentioning that this is a potentially source-breaking change. If a user in 2.12 has:

// `Seq` is the default `scala.Seq`
val a: Seq[Food] = orderFood(myOrder)

Then that will no longer compile in 2.13

Copy link
Member

@lrytz lrytz May 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it might be a good strategy for APIs to only move to immutable.Seq in the result type, but keep accepting collection.Seq arguments, if the method implementation can work fine with mutable collections.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this in 2b4de2d.


~~~ scala
import scala.collection.Seq
~~~

In the future when your API is able to break the source compatibility, it might also make sense to migrate towards the `scala.collection.immutable.Seq` ("ISeq") for both Scala 2.12 and Scala 2.13.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't seem like you refer to "ISeq" or "CSeq" anywhere, so the parentheticals can probably be removed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added commits that ended up adding references to ISeq and CSeq.


~~~ scala
import scala.collection.immutable.Seq
~~~

Note that in Scala 2.13 the sequence passed into as a varargs as `orderFood(xs: _*)` must also be immutable. This is because the sequence passed in as a varargs must conform to `scala.Seq` according to [SLS 6.6](https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#function-applications). Thus, if your API exposes varargs it will be an unavoidable breaking change. This might affect Java interoperability.

## What are the breaking changes?

The following table summarizes the breaking changes. The "Automatic Migration Rule" column gives the name of the migration rule that can be used to automatically update old code to the new expected form.
Expand Down Expand Up @@ -77,7 +102,6 @@ Some classes have been removed, made private or have no equivalent in the new de
Other notable changes are:

- `Iterable.partition` invokes `iterator` twice on non-strict collections and assumes it gets two iterators over the same elements. Strict subclasses override `partition` do perform only a single traversal
- `scala.Seq[+A]` is now `scala.collection.immutable.Seq[A]` (this also affects varargs methods).
- Equality between collections is not anymore defined at the level of `Iterable`. It is defined separately in the `Set`, `Seq` and `Map` branches. Another consequence is that `Iterable` does not anymore have a `canEqual` method.
- The new collections makes more use of overloading. You can find more information about the motivation
behind this choice [here](http://scala-lang.org/blog/2017/05/30/tribulations-canbuildfrom.html). For instance, `Map.map` is overloaded:
Expand Down Expand Up @@ -236,4 +260,4 @@ Examples of libraries that cross-compile with separate source directories:
To learn about differences when implementing custom collection types or operations, see the following documents:
- [The architecture of Scala collections]({{ site.baseurl }}/overviews/core/architecture-of-scala-213-collections.html)
- [Implementing custom collections]({{ site.baseurl }}/overviews/core/custom-collections.html)
- [Adding custom collection operations]({{ site.baseurl }}/overviews/core/custom-collection-operations.html)
- [Adding custom collection operations]({{ site.baseurl }}/overviews/core/custom-collection-operations.html)