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
Seqcomes 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
otherwisebranch 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
Seqchange. -
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 theSeqoriginates from a library. -
If that’s hard, a quick and dirty
greppipeline or similar would be worth doing. -
Be ready for libraries changing to the new
Seqor 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.