-
Notifications
You must be signed in to change notification settings - Fork 72
FAQ
This page should evolve over time, but already be useful now. When more official documentation is available, content can be replaced by links.
The following table summarizes the main changes:
Description | Old Code | New Code | Automatic Migration Rule |
---|---|---|---|
Method to[C[_]] has been removed (it might be reintroduced but deprecated, though) |
xs.to[List] |
xs.to(List) |
NewCollections |
SortedSet : the to , until and from methods are now called rangeTo , rangeUntil and rangeFrom , respectively |
xs.until(42) |
xs.rangeUntil(42) |
|
immutable.Set/Map : the + operation no longer has an overload accepting multiple values |
Set(1) + (2, 3) |
Set(1) + 2 + 3 |
|
mutable.Set/Map no longer have a + operation |
mutable.Set(1) + 2 |
mutable.Set(1).clone() += 2 |
|
mutable.Set/Map no longer have an updated method |
mutable.Map(1 -> 2).updated(1, 3) |
mutable.Map(1 -> 2).clone() += 1 -> 3 |
|
collection.Set/Map no longer have + and - operations |
xs + 1 - 2 |
xs ++ Set(1) -- Set(2) |
|
collection.Map no longer have -- operation |
|||
mapValues and filterKeys now return a MapView instead of a Map
|
kvs.mapValues(f) |
kvs.mapValues(f).toMap |
|
Traversable and TraversableOnce are replaced with Iterable and IterableOnce , respectively |
def f(xs: Traversable[Int]): Unit |
def f(xs: Iterable[Int]): Unit |
NewCollections |
Stream is replaced with LazyList
|
Stream.from(1) |
LazyList.from(1) |
NewCollections |
Seq#union is replaced with concat
|
xs.union(ys) |
xs.concat(ys) |
|
Stream#append is replaced with lazyAppendAll
|
xs.append(ys) |
xs.lazyAppendAll(ys) |
NewCollections |
IterableOnce#toIterator is replaced with IterableOnce#iterator()
|
xs.toIterator |
xs.iterator() |
NewCollections |
copyToBuffer has been deprecated |
xs.copyToBuffer(buffer) |
buffer ++= xs |
NewCollections |
Iterable no longer has a sameElements operation |
xs1.sameElements(xs2) |
xs1.iterator().sameElements(xs2) |
|
TupleNZipped has been replaced with LazyZipN
|
(xs, ys).zipped |
xs.lazyZip(ys) |
NewCollections |
collection.breakOut no longer exists |
val xs: List[Int] = ys.map(f)(collection.breakOut) |
val xs = ys.view.map(f).to(List) |
|
LinearSeq no longer has a companion-apply |
LinearSeq(1, 2, 3) |
List(1, 2, 3) |
|
MapProxy , SetProxy , SynchronizedX no longer exist |
|||
SeqForwarder , MutableList , StreamView no longer exist |
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. See https://github.com/scala/scala-collection-compat/ for more details on how to use it.
Other notable changes are:
-
Iterable.partition
invokesiterator
twice on non-strict collections and assumes it gets two iterators over the same elements. Strict subclasses overridepartition
do perform only a single traversal -
scala.Seq[+A]
is nowscala.collection.immutable.Seq[A]
(this also affects varargs methods). -
The new collections makes more use of overloading. You can find more information about the motivation behind this choice here. For instance,
Map.map
is overloaded:scala> Map(1 -> "a").map def map[B](f: ((Int, String)) => B): scala.collection.immutable.Iterable[B] def map[K2, V2](f: ((Int, String)) => (K2, V2)): scala.collection.immutable.Map[K2,V2]
Type inference has been improved so that
Map(1 -> "a").map(x => (x._1 + 1, x._2))
works, the compiler can infer the parameter type for the function literal. However, using a method reference in 2.13.0-M4 (improvement are on the way for 2.13.0) does not work, and an explicit eta-expansion is necessary:scala> def f(t: (Int, String)) = (t._1 + 1, t._2) scala> Map(1 -> "a").map(f) ^ error: missing argument list for method f Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing `f _` or `f(_)` instead of `f`. scala> Map(1 -> "a").map(f _) res10: scala.collection.immutable.Map[Int,String] = ChampHashMap(2 -> a)
-
View
s have been completely redesigned and we expect their usage to have a more predictable evaluation model. You can read more about the new design here.
scala.collection.immutable.ImmutableArray
is an immutable sequence backed by an array. It is used to pass varargs parameters.
The scala-collection-contrib
module provides decorators enriching the collections with new operations. You can
think of this artifact as an incubator: if we get evidence that these operations should be part of the core,
we might eventually move them.
The following collections are provided:
-
MultiSet
(both mutable and immutable) -
SortedMultiSet
(both mutable and immutable) -
MultiDict
(both mutable and immutable) -
SortedMultiDict
(both mutable and immutable)
The following new operations are available:
def groupMap[K, B](key: A => K)(f: A => B): Map[K, CC[B]] // (Where `CC` can be `List`, for instance)
def groupMapReduce[K, B](key: A => K)(f: A => B)(g: (B, B) => B): Map[K, B]
groupMap
is equivalent to groupBy(key).mapValues(_.map(f))
.
groupMapReduce
is equivalent to groupBy(key).mapValues(_.map(f).reduce(g))
.
Additional operations are provided by the scala-collection-contrib
module. You can
think of this artifact as an incubator: if we get evidence that these operations should be part of the core,
we might eventually move them.
The new operations are provided via an implicit enrichment. You need to add the following import to make them available:
import strawman.collection.decorators._
The following operations are provided:
-
Seq
intersperse
-
Map
-
zipByKey
/join
/zipByKeyWith
-
mergeByKey
/fullOuterJoin
/mergeByKeyWith
/leftOuterJoin
/rightOuterJoin
-
Are there new implementations of existing collection types (changes in performance characteristics)?
The default Set
and Map
are backed by a ChampHashSet
and a ChampHashMap
, respectively. The performance characteristics are the same but the
operation implementations are faster. These data structures also have a lower memory footprint.
mutable.Queue
and mutable.Stack
now use mutable.ArrayDeque
. This data structure supports constant time index access, and amortized constant time
insert and remove operations.
We will eventually provide a CrossCompat
migration rule (see scala/scala-collection-compat#19) that
will rewrite old code into code that cross-compiles with 2.13 and with older Scala versions.
That being said, we expect most usages to be compatible with 2.13 (at the cost of some warnings, sometimes).
If your code does not compile with 2.13, you can migrate it and use the scala-collection-compat
module to make the
new code compile with older Scala versions.
Finally, if your code uses features that are not supported by the scala-collection-compat
library (typically, if you implement a custom collection type),
you will have to use different source directories per Scala version:
// Adds a `src/main/scala-2.13+` source directory for Scala 2.13 and newer
// and a `src/main/scala-2.13-` source directory for Scala version 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.13-"
}
}
Examples
- https://github.com/scala/scala-parser-combinators/pull/134/files
- https://github.com/scala/scala-xml/compare/master...lrytz:newCollections?expand=1
- Some other examples are listed here: https://github.com/scala/community-builds/issues/710
-
ArraySeq
is deprecated. 2.12 had bothWrappedArray
andArraySeq
, whereWrappedArray
has specialized subclasses for primitive arrays.ArraySeq
is backed by an object array, primitives are boxed. In 2.13,WrappedArray
can be used either way for primitive arrays (TODO: document how).
CanBuildFrom
was used to:
- generically implement transformation operations whose return type could
vary according to the type of collection elements (ie mapping a
Char
to aChar
in aString
returns aString
, but mapping aChar
to anInt
returns aIndexedSeq[Int]
), - abstract over the arity of collection type constructors (ie
List[_]
vsMap[_, _]
), - abstract over implicit parameters required to perform a transformation operations (ie mapping an
A
to aB
inSortedSet[A]
requires an implicitOrdering[B]
to return aSortedSet[B]
), - provide type-driven builders to implement generic transformation methods (eg
Future.traverse
).
The first three points are now handled using overloading. This means that Map[K, V]
has two overloads of map
,
one that takes a (K, V) => (K', V')
mapping function and returns a Map[K', V']
, and one that takes a
(K, V) => A
mapping function and returns an Iterable[A]
.
You can find more information about the design here.
To address the last point, there is a BuildFrom
typeclass that works exactly like the former CanBuildFrom
. See the next section
for examples of use.
Depending on the level of desired genericity, several solutions can apply, with different complexity.
Take an IterableOnce[A]
as parameter, or an Iterable[A]
if you need more than one
traversals:
implicit class SumByOperation[A](coll: IterableOnce[A]) {
def sumBy[B](f: A => B)(implicit num: Numeric[B]): B = {
val it = coll.iterator()
var result = f(it.next())
while (it.hasNext()) {
result = num.plus(result, it.next())
}
result
}
}
Use BuildFrom
:
def optionSequence[CC[X] <: Iterable[X], A, To](xs: CC[Option[A]])(implicit bf: BuildFrom[CC[Option[A]], A, To]): Option[To] =
xs.foldLeft[Option[Builder[A, To]]](Some(bf.newBuilder(xs))) {
case (Some(builder), Some(a)) => Some(builder += a)
case _ => None
}.map(_.result())
The optionSequence
operation can be used on any collection (but not on an Array
or a String
):
scala> optionSequence(List[Option[Int]](Some(1), Some(2), Some(3)))
res1: Option[List[Int]] = Some(List(1, 2, 3))
scala> optionSequence(Set[Option[(Int, String)]](Some(1 -> "foo"), Some(2 -> "bar")))(TreeMap) // Force the result type
res4: Option[scala.collection.immutable.TreeMap[Int,String]] = Some(TreeMap(1 -> foo, 2 -> bar))
For more advanced cases, or your operation should also work with Array
s, String
s and View
s,
the pattern given in the previous section is not enough.
The scala-collection-contrib
module
provides a more advanced machinery that handles that:
class IntersperseOperation[C, S <: HasSeqOps[C]](coll: C)(implicit val seq: S) {
def intersperse[B >: seq.A, That](sep: B)(implicit bf: BuildFrom[C, B, That]): That =
bf.fromSpecificIterable(coll)(new View.Intersperse(seq(coll), sep)) // (Assume that there is a `View.Intersperse` implemented somewhere)
}
implicit def IntersperseOperation[C](coll: C)(implicit seq: HasSeqOps[C]): SeqDecorator[C, seq.type] =
new IntersperseOperation(coll)(seq)
This pattern makes the intersperse
operation available to any Seq
like type (eg. a SeqView
, an Array
or a String
).
Read the draft document on the design of the Scala 2.13 collections: https://github.com/scalacenter/docs.scala-lang/blob/collection-strawman/_overviews/core/architecture-of-scala-collections.md
You want to add overloads to specialize a transformation operations such that they return a more specific result. Examples are:
-
map
, onStringOps
, when the mapping function returns aChar
, should return aString
(instead of anIndexedSeq
), -
map
, onMap
, when the mapping function returns a pair, should return aMap
(instead of anIterable
), -
map
, onSortedSet
, when an implicitOrdering
is available for the resulting element type, should return aSortedSet
(instead of aSet
).
The following table lists transformation operations that might return a too wide type. You might want to overload these operations to return a more specific type.
Collection | Operations |
---|---|
Iterable |
map , flatMap , collect , scanLeft , scanRight , groupMap , concat , zip , zipAll , unzip
|
Seq |
prepended , appended , prependedAll , appendedAll , padTo , patch
|
immutable.Seq |
updated |
SortedSet |
map , flatMap , collect , zip
|
Map |
map , flatMap , collect , concat
|
immutable.Map |
updated , transform
|
SortedMap |
map , flatMap , collect , concat
|
immutable.SortedMap |
updated |