You know the routine for porting from Scala 2.12 to 2.13: you’ll get a bunch of compiler errors and warnings, and you can quickly enough hack your way through them.
There is, though, one runtime issue I’ve bumped into. When pattern matching on Seq
from a library, you need to be careful about what kind of a Seq
you’ve been handed.
Good news
The migration guide for 2.13 explains that the scala.Seq[+A]
alias has changed from scala.collection.Seq[A]
to scala.collection.immutable.Seq[A]
. (That’s a great guide, BTW, and you’ll want to read it).
This is good news because the Seq
you get by default in Scala 2.13 is immutable.
The issue
The short-term cost to this gain is an issue under specific circumstances.
If:
- you use pattern matching on a
scala.Seq
(orIndexedSeq
) in 2.12 - where the
Seq
comes from a library - and you upgrade to 2.13
…then you may find your patterns no longer match at runtime.
The reason is that in 2.12 your Seq
is the agnostic scala.collection.Seq
,
but in 2.13 it’s the strong scala.collection.immutable.Seq
.
The library, however, may still be giving you a scala.collecton.Seq
.
The 2.13 default Seq
is specifically immutable, which has more constraints on it that scala.collection.Seq
.
They won’t match in a pattern.
To be clear, you should check what kind of Seq
you’re getting.
A library might be designed to give you an immutable Seq
,
in which case you’ll have no problems.
Example
Consider pattern matching on Seq
handed to us in a JsArray
from Play JSON:
// Using "com.typesafe.play" %% "play-json" % "2.8.1"
import play.api.libs.json._
def main(args: Array[String]): Unit = {
// We will parse this into `JsArray(scala.collection.IndexedSeq)`
val json = Json.parse(""" [ "hat", "dog" ] """)
json match {
case JsArray(Seq(s1, s2)) => println(s"Matched: $s1, $s2")
case otherwise => println(s"Unexpected: $otherwise")
}
}
What does this code do?
-
Compiled with 2.12, this code prints
Matched: hat, dog
. -
Under 2.13, the
otherwise
branch matches.
You can try it in Scastie, adjusting the compiler in “Build settings”.
The fix in this case could be to specify which Seq
we mean:
case JsArray(scala.collection.Seq(s1, s2)) =>
println(s"Matched: $s1, $s2")
In other words, we’ve loosened the match, no longer demanding an immutable Seq
.
Perhaps in the future (issue 388) the library will return an immutable Seq
.
Recommendations
-
Do read the migration guide which contains several options for handling the
Seq
change. -
Your tests can help you catch this, and code coverage can help you find gaps. Search your code coverage looking for uncovered matches on
Seq
, where theSeq
originates from a library. -
If that’s hard, a quick and dirty
grep
pipeline or similar would be worth doing. -
Be ready for libraries changing to the new
Seq
or adding variations of methods that return the newSeq
.
I’m not aware of a scalafix for this change, but if anyone knows of one, do share it.
Originally posted on Richard’s site.