Saturday, October 30, 2010

Using Groovy and Scala, my first year

On Groovy

After programming almost exclusively in Java for the last 14 years, I picked up Groovy during the spring of 2010. If my memory serves me correctly, it took me about a week. Groovy is a powerful and worthy language. Many things which are hard to do in Java are easy in Groovy. The ExpandoMetaClass makes it very easy to write amazingly powerful DSLs in a jiffy. To give you a concrete example, I could replicate logback's XML-based configuration sub-system of about 10'000 lines of rather intricate code with about 500 lines of Groovy. The groovy-based configuration DSL for logback is better than its XML counterpart in every respect. Not only is the groovy syntax much shorter than XML, it is also internal. This means that the logback configuration DSL preserves all the power of Groovy, a fully-fledged programming language. In short, Groovy is fantastic for writing internal DSLs.

Now the bad news. Contrary to Java, Groovy has a dynamic type system which is another way of saying that Groovy has dynamic dispatch. Dynamic-dispatch allows for a lot of flexibility as offered by ExpandoMetaClass and mixins but at the cost of a very significant performance hit. Last but not least, the Groovy compiler skips type checking which leaves you to discover errors at runtime.

A basic for-loop can be 1000 times slower to execute in Groovy than in Java. I don't think it is possible to build performance critical systems using Groovy. But I might be wrong and my groovy-is-slow gripe being as irrelevant as C++ folks complaining about the "slowness" of Java byte code.

By the way, my current IDE of choice is IntelliJ IDEA which has pretty decent support for Groovy as well as Scala. I switched away from Eclipse because of its shoddy support for Groovy and even worse support for Scala.

On Scala

Compared to Groovy, Scala has been harder to learn for me. While Groovy can be learned piecemeal, one has to know a lot of Scala before being able program in Scala. I think this is true for any new language, groovy being a rare-exception since it is essentially a super-set of Java.

In exchange for a steeper learning curve, Scala rewards the apprentice with what seems like a fresh perspective on programming: the functional view. Like Java, Scala is statically typed which means that Scala generated code can be as efficient as Java generated code.

As statically-typed language, the Scala compiler catches many programming errors early in the development cycle. However, thanks to its amazingly powerful type-inference system scala dispenses with much the boilerplate that is found in other statically typed languages. Scala also provides strong support for writing internal DSLs. I have written a configuration DSL for logback in Scala. The still unpublished result is as flexible and powerful as its Groovy counterpart and only slightly more verbose. On the other hand, the Scala version is strongly-typed which means that one can write logback configuration files with code-completion provided by the IDE. Is that cool or what?

Scala has a long series of very neat features. I like Scala to the point of animating the Scala Enthusiasts Group in Lausanne, Switzerland. Yes, the Scala requires some effort to learn. Yes, the Scala compiler is slower than most. Yet, those disadvantages are compensated by the consistency and the sheer expressive power of the Scala language.

On the downside, Scala has a consistent history of braking compatibility between versions. For example, code compiled with Scala version 2.7 will not work with Scala version 2.8. Starting with Scala version 2.9, the Migration Manager will purportedly address this binary-format incompatibility problem.

However, from what I understand, Scala suffers from a much hairier compatibility problem. Indeed, Scala traits are copied/folded into code importing the trait. This means that client code compiled with Scala version 2.x.y-revision_A might not be compatible with Scala version 2.x.y_revision_B (with x and y unchanged) if some trait changed between releases.

If this assertion is true, and I pray the God of Evolution that it is false, then
Scala suffers from a fatal flaw. There is no way an ecosystem of libraries written in Scala can prosper when one has to worry about the micro release version of Scala used to compile a given library. Applications using just a couple of libraries written in Scala would be unmanageable. It is pretty ironic that Scala forfeits one of Java's crucial advantages, namely cross-platform compatibility, in exchange for multiple-inheritance as provided by traits.

Again, I may be completely misunderstanding the implications of Scala traits. If you know better about how Scala traits work, you are hereby invited to set the record straight.

Update

According to new information I received, it appears that traits are imported via stubs. Let A be a class importing a trait T. The compiler will create a stub for T in A and invocations of T's methods in A will be delegated to an encapsulated instance of T by the stub. The code in T is not copied into A, only T's method signatures are directly visible in A.

It follows that A compiled with one version of Scala and T will be compatible with a different version of Scala as long as T's interface, i.e. method signatures, remains the same (or change in a compatible way) and the Scala language versions are binary compatible.

In other words, traits are not a source of new incompatibility issues as long as the method signatures of a trait are not changed in incompatible ways, much like Java interfaces can only change in certain limited ways in order to preserve compatibility.


QOS.ch, main sponsor of cal10n, logback and slf4j open source projects, is looking to hire talented software developers. If interested, please email your resume to hr@qos.ch.