This is the second post of a two-parter. Please read part 1 for context and additional information.
3rd Party Libraries
As mentioned in part 1, Scala is not binary backwards-compatible between major releases. Although code compiled with 2.10 may work properly with libraries compiled against 2.9.x, this is hardly recommended and may result in a lot of subtle error conditions at runtime that are hard to track down. As a result, the recommended practice is to upgrade all Scala dependencies to versions compiled against 2.10. Fortunately the Scala ecosystem is very active, so most common libraries had 2.10 builds on announcement day, but you are still likely to encounter libraries that need to be upgraded because:
- They're lagging behind the Scala release schedule and simply haven't released 2.10-compatible binaries (fortunately, this seems to be extremely rare; in practice all our dependencies were otherwise resolved);
- The specific version you're using does not have a 2.10-compatible release and likely never will (Squeryl 0.9.5-2);
- A 2.10-compatible official release is out but hasn't yet been published to the central repository (Scalatra 2.0.5).
My experience shows that with a bit of dilligence, practically all our (myriad) dependencies were fairly easy to migrate to 2.10. In most cases this simply entails switching to the appropriate artifact ID suffix (_2.10 instead of _2.9.2, see part 1 for associated rant), in other cases I had to upgrade to a later official release and in rare cases I had to dig in and replace a dependency with an appropriate fork or snapshot version. Here is a list of migration notes for some of our dependencies:
Library | Previous | New version | Comments |
Scala-Time | 0.6 | nscala-time 0.2 | The original library appears to be abandoned and was forked on GitHub. The package changed from org.scala-tools.time to com.github.nscala-time.time |
ScalaMock | 2.4 | 3.0.1 | The 3.x series runs natively on Scala 2.10 without proxy factories and the like. Syntax is a bit odd but works like a charm. The ScalaTest integration is compiled against ScalaTest 2.0-M5b, which forced us to upgrade. |
ScalaTest | 1.8 | 2.0-M5b | Minor API changes: Spec→FunSpec, parameters changed in Suite.run() |
Squeryl | 0.9.5-2 | 0.9.5-6 | |
Argot | 0.4 | 1.0.0 | |
Scala I/O | 0.4.1-seq | 0.4.2 | |
Lift JSON (+ext) | 2.4 | 2.5-M4 | |
Lift Facebook | 2.4 | 2.5-SNAPSHOT | Had to exclude net.liftweb:lift-webkit_2.10 to avoid dependency conflicts |
Akka | 2.0.4 | 2.1 | Scheduling now requires an implicit ExecutionContext. See migration guide for details. |
Scalatra | 2.0.4 | 2.0.5-SNAPSHOT | Official 2.0.5 is out, but has not yet shown up on the central repository, so I ended up going with the (frozen) 2.0.5 snapshot artifacts |
Scalatra | 2.1.1 | 2.2.0 | scalatra-akka module has been folded back into the core artifact as of 2.2.0. |
Graph for Scala | 1.5.1 | 1.6.1 | |
Scala Library Changes
The Scala class library itself has a number of changes you ought to be aware of before migrating to 2.10. The really big change is that Scala actors are deprecated in favor of Akka. You can still use them by importing the scala-actors artifact from the Scala 2.10 distribution, but it is recommended to migrate fully to the new actor system as this is also likely to be obsoleted by 2.10.1. The gentle folk at Typesafe have provided a very comprehensive migration guide to assist your efforts.
The less prevasive API changes we ran into include:
- List.elements is deprecated in favor of List.iterator;
- TraversableOnce.toIndexedSeq no longer takes a type argument. This was actually quite pervasive in our codebase, causing plenty of compilation errors, and is easily worked around by removing the type parameter (which is extraneous to begin with);
- Scala actors' Actor.receive method is now public (previously protected). This had to be rectified in pretty much all of our existing actors by removing the protected modifer;
- Occasional subtle API changes requiring minor code fixes. For example, see this question on StackOverflow.
Summary
Opinions differ among members of our team - some predicted that the migration process will be much more complex whereas personally, given the relatively high level of maturity I've come to depend on in the 2.9 series, the migration process actually ended up being significantly harder than I anticipated. Particularly disconcerting were the occasional compiler failures which took a lot of time to track down. Practically speaking, though, the whole migration process took less than 3 days (including documentation), did not involve additional teammates, and all problems were either resolved or worked around rather quickly. The Typesafe compiler team has been very helpful in analyzing and resolving the single bona-fide compiler bug we've run into, and the community as a whole (on Stack Overflow, Google Groups and elsewhere) was also extremely supportive and helpful.
On the whole, am I happy with the process? So-so. There is nothing here an experienced Scala developer should have serious trouble with, but it will take a lot more stability and predictability for Scala to gain mainstream acceptance in the industry, and that includes a much easier and more robust migration path to upcoming releases (yes, that includes migrating from 2.10 to 2.11 when it comes along). That being said, Scala has been a terrific language to work with in the last couple of years, and the new features in 2.10 (particularly reflection, macros and string interpolation) should make this an extremely worthwhile upgrade. We still have a lot of regression testing to do on the new version, and if anything interesting pops up I'll be sure to post about it separately (or bitch about it on Twitter...)