A couple of months ago, I posted the question: “Is Scala ready for the enterprise?” with the answer ‘no’ at the time. I called out specific mention to the fact that 2.8 was once again not going to be backward compatible with the previous release; the post generated a number of comments on the subject.
Hoping to help Scala move towards becoming more suitable for the enterprise, I proposed modularity for scala. The goal was to take the existing runtime, break it down into component modules, and set up versioning between them. The idea was to be able to evolve modules independently of each other, so that a core kernel of Scala library support would be usable and then Scala systems could import modules on an as-needed basis.
However, despite posting a set of requirements (and for a while, hosting them in Wiki form on GitHub), it became clear that modularisation in Scala won’t work whilst basic issues like versioning are swept under the carpet. Sadly, this just highlights the fact that the release engineering process of Scala is not sufficiently mature to base any kind of serious application on it; at least, not one that would track the core libraries changing going forwards.
The issue at stake is the subsequent version of Scala to 2.7. At present, it has been described as Scala 2.8 and brings a number of new features (named and default arguments) which themselves require a binary incompatible change to the way that methods are invoked.
There’s a currently accepted definition of version numbers which almost everyone (bar Sun and Oracle) uses. That is formalised in the OSGi specification; major.minor.micro, where major increments represent a step-change in compatibility, minor increments represent backwardly compatible behaviour but often bring new features, and micro which adds no new functionality but fixes bugs. Sun shot themselves in the foot a long time ago with the Java 2™ trademark, which at a marketing stroke killed off any possibility of using a Java 2.x version in the future. (I’ve covered the folly of confusing marketing numbers and version numbers before.)
Although Scala has suffered glitches before (2.5 actors were incompatible with 2.6, for example) the ongoing progression of versions had largely worked. The introduction of generics caused the last biggest headache of Scala (in 2.7) but like the previous time it happened, it was seen as the last time we’d need to change backward compatibility.
Normally, a project would bump up its major version number to signify these changes. By rights, we should be on about Scala 4.0 by now. Consumers of Scala should then be able to determine whether they should upgrade to the latest-and-greatest, or accept the fact that they were on a binary incompatible version and continue on. Hiding such changes behind a 2.x stream is following exactly the same mistake that Java made back in 1997; an irrational fear of bumping the major version number. In fact, when asked on the scala-internals list, the responses included “We keep to the name 2.8 mainly because it was announced that way for some time. Switching to 3.0 now would cause confusion” and “I agree that too much has been said and printed about Scala 2.8 to, at this point, change it to Scala 3.0” – the latter of which was cited as “An even stronger argument”. It’s pretty unlikely that a bump would cause any confusion whatsoever; after all, it’s the case that both 2.8 and 3.0 would supersede 2.7. However, the interpretation of those semantics is important; someone choosing to use 2.8 might think that it is backwardly compatible, but would think 3.0 isn’t necessarily.
This of course boils down to the key problem with Scala as it stands today; the version numbering system is based on number of previous uses of a term and gut feeling rather than a more objective measurement of the (in)compatibility of code. None of this will matter to the (early) adopters of Scala; they’ll just notice when they upgrade their runtime and discover that it suddenly doesn’t work.
So, back to modularity. Unfortunately, with no sane versioning system in place for the Scala runtime as a whole, it will be impossible to create a set of modules for the Scala language that are anything more than a shim which splits up the library.jar into a number of tightly coupled dependants. The only way for Scala modularity to survive would be by evolving modules independently; but since these are tightly coupled to the version of the compiler and runtime used, such parallel evolution will not happen in any meaningful way. In fact, the existence of implicits and the possibility of misuse of traits (without proper evolution procedures) will mean that instead of allowing modular development, the Scala compiler and runtime implicitly enforces a total lockdown of any versions of code that may run. Even now, the head of the Scala build is being co-compiled against both Java 5 and Java 6 because the output (and runtime requirements) for both are different. In any case, it doesn’t make sense to pursue modularity in Scala whilst version numbering and binary incompatibilty between releases run rampant throughout the Scala codebase.
Scala is an interesting language. It challenges a lot of the preconceived notions that other languages have avoided; and it’s surpassing the slowly fossilising Java language thanks to its ability to support functional constructs like lambdas and filter/map operations. However, the whole release process and long term viability of the language and runtime remains questionable even in the face of high-profile adopters. For the enterprise, the choice is clear – Scala is not ready for the enterprise.