With Scala you feel smart having just got something to work in a beautiful way but when you look around the room to tell your clojure colleague how clever you are, you notice he left 3 hours ago and there is a post-it saying use a Map. Daniel Worthington-Bodart
I consider it an “inhumane” language. That is, one that I’m surprised humans created and intended for other humans. Clinton Begin
I first encountered Scala about six years ago when I was forced to use an in-house build system written in Scala, that also used Scala as the language for build scripts. At the time, I thought Scala was a really poor choice.
Our build scripts took three minutes to compile when changed — before actually starting the build.
The syntax for the build scripts was really opaque. Sometimes it was
def, sometimes it was a
val. Sometimes there was a
=, sometimes braces. On the whole, it looked like someone had wandered through my build script dropping random pieces of punctuation.
But, I could accept that this may have just been the wrong use of a good language, possibly also implemented badly. For instance, build scripts are just something you dip into occasionally. That’s not a great way to learn a complex language. Besides, I’d always really enjoyed type inference when I’d used Haskell.
In the last two weeks I’ve been building a simple RESTful API for a complex data set using Scala. I don’t ever want to touch Scala again.
We picked Scala because the existing team were writing Java and XSL and doing a lot of XML processing. Scala seemed to be an easy path forwards, with a simpler ramp-up than the alternatives. It also seemed that the unique in-language XML support would be really helpful.
Honestly, it’s disappointing to be writing this. I thought I was done with pontificating on programming languages.
I have a number of complaints.
I knew about this going in. We warned the client about it. We thought it would be avoidable as we were planning on writing a series of small apps and services. We were wrong. It is essentially impossible to practice TDD in Scala simply due to the time it takes to compile. Though we were using Gradle in daemon mode, test runs on a tiny code base could still take a minute 20 seconds. This is, frankly, ridiculous.
Libraries and the Community
The Scala community, however, is operating at peak CADT. Documentation is terrible or non-existent. Libraries move from milestone to release candidate to snapshot to milestone to replacement.
Partially, this is understandable. Someone has decided that Scala’s
flexible syntax and method invocation syntax mean it’s a great
language for creating DSLs. Every library has selected it’s own set of
operators and composition philosophy. This leads to ridiculous
decisions such as using
~ to glue handlers together in the flexible
DSL offered by spray — chosen because it’s a visually
lightweight character. Or the attempt at a Gherkin syntax that you can
see in specs2.
These libraries are hard enough to start using. I can’t imagine what they’re like to contribute to. I’d be angling for a re-write, too.
But mostly, I wish the language community would start to rate stability as a valid goal.
Over the years I’ve found that the easiest programming languages to get started with are those with the ‘simplest’ syntax. Simplicity is a hard thing to define, but in this case it is not the same as flexible. It’s actually closer to regular, or limited with extension options. I’ve found Lisp, Ruby and HyperTalk to be great examples. I’ve found AppleScript and Perl to be terrible.
Scala is easily the worst I’ve seen. That punctuation-strewn build system I was forced to use was just taking advantage of Scala’s ridiculously flexible syntax. The goal appears to be to allow concise expression of your program: arbitrary operators, infix method calls, flexible anonymous function definition, and something weird with attaching anonymous functions to method calls.
Scala is not a simple language.
This issue reached its pinnacle for us when we were unable to figure out how to replicate the magic response composition syntax used by Spray to abstract away the addition of CORS headers to HTTP responses. A simple approach to this sort of abstraction is the core responsibility of an HTTP app framework.
Sure, with another couple hours we would have been able to figure it out. But the combination of magic syntax, poor documentation of the core concepts Spray was applying to an HTTP pipeline, slow builds and the habits around types was just killing us. We ended up copying and pasting the same four lines into all our endpoints. A dark moment.
Everything is a Type
Scala programmers really like types. This has lead to some surprising decisions. The oddest we encountered was the plethora of types blossoming around the HTTP request/response cycle. Instead of two simple maps with keys for status, headers and body there were types. Types galore! There was a request context, a request, a response, a large number of directives and even types for every ‘known’ HTTP header.
The known HTTP header part was particularly ridiculous. HTTP headers are defined as simple key/value pairs. You’re supposed to be allowed to add arbitrary headers. You’re not supposed to be constrained by the framework you’re using not having any support for CORS yet.
Of course, this isn’t a criticism of Scala-the-language. But the landscape that the common habits present you with form an equal part of the experience of using that language.
‘Local’ Type Inference
And this is the real death knell for Scala. Hindley-Milner Type Inference is a fantastic thing. Any serious programmer should learn a language with this at some point. It’s enabled monads, which you want to be able to understand and then use in a type-free language.
In my mind, Hindley-Milner was always the promise and the pay-off for Scala. And now I’ve discovered it’s not really there. I know this is the fault of the JVM’s use of type erasure. I don’t care.
The workflow I ended up with was to write a function without any type
annotations. Ignore all the red lines of the IDE’s confusion. Compile
the code. In the output look for a warning about
(Long => Int)
return value being ignored in a function returning
(Long => Int)
into my code as the return type annotation of my
function. Compile again.
Believe me, I’m using a significantly simpler return type than even
very simple code will quickly encounter. And while I’m on the topic,
thanks for making me care about the difference between
int, again. It’s been far too long since I wrote C.
And remember those build times in all this, too.
The issue here is that Scala can’t perform full type inference across your program. As a result, you very frequently have to provide a type annotation to help the compiler out. This is not how type inference is supposed to work. I really don’t like pasting in opaque incantations that are for the computer, not humans.
What’s that DevOps line about the computers getting together at night and laughing at us?
There are other things I could talk about. The confused array of build tools. The hopeless confusion of even the most powerful of IDEs. The enormous array of types of types. The horrible repetition required by case classes.
But I think these are enough issues to describe. These alone significantly slowed our efforts in a mere two weeks. These were enough that I do not want to ever touch Scala again.
If you’ve read all the way to the end you may have noticed some themes. My main complaint with Scala is that it is a simply inaccessible language. My theory is that it was designed to experiment with advanced type and language features first, and only secondly became a language intended to be widely used.
And in my mind, that’s just not a good enough pedigree to warrant using. There are many other languages that let you explore functional styles. Or, if you’re really keen to understand true strong type systems (and you should be) then learn Haskell. I don’t know, maybe — just maybe — you could get the same out of learning Scala.
But for the love of all that you find sacred, don’t use it in production.