Keep the JVM, dump the rest (Scala+Play+MongoDB)
I decided to try three new things at once and get back into some server-side development.
- Scala uses the JVM for the hard work, but feels more like Ruby or Python. It’s statically-typed, but for the most part infers types rather than making you type them in.
- Play ignores the Tomcat/J2EE legacy in favor of a more modern approach — “just press reload” to see changes.
- MongoDB dodges the ORM problem by storing objects in the first place. People like JSON, so the database should store JSON. Done!
I was curious whether I could lose all traces of Enterprise Readiness and use the JVM with a nice language, a nice web framework, and no annoying SQL/ORM gunge.
TL;DR Answer: Promising, but lots of rough edges.
Some disjointed thoughts follow.
I like Scala a lot.
If you aren’t familiar with it, Scala is a pragmatic language design.
- it compiles to Java bytecode and runs on the JVM. Thus, no headaches for operations team, no need to port it to new architectures, quality JIT and GC, etc. A Scala app looks like a regular Java jar/war/ear/*ar.
- you can use any existing Java library from Scala with no special effort, just import it.
- it’s statically typed, but sophisticated generics and type inference keep the code looking clean and concise.
- it supports both functional and OO styles, sanely mixing the two.
- it has multiple inheritance (mixin traits) that works properly, thanks to linearization.
- it has a basket of features that save typing, such as pattern matching and automatically creating accessors for fields.
- language features give you ways to avoid Java’s preprocessor/reflection/codegen hacks (think aspect-oriented programming, dependency injection, Kilim, and other extralinguistic hacks)
While C# fixes Java by adding a bunch of features but keeping a pretty similar language design, Scala fixes Java by trying to bring the conciseness and fun people find in Ruby, Python, Haskell, or whatever onto the JVM.
There’s a popular controversy about whether Scala is too complex, and just reading about it I had the same fear.
- I found I could get a web app working without hitting any complexity hiccups. You don’t have to design a DSL or understand variance annotations to write a web app.
- I didn’t get any mystery error messages of the kind I used to get all the time in the early days of C++.
- I’m not sure it’s more complex than Java if you include the usual stack of extralinguistic hacks in Java, and I doubt Scala is a larger language than C#.
Scala’s Option type wasn’t very helpful.
Scala has thing called Option, which I believe Haskell calls a Maybe type. It’s a container that contains a single value or else nothing. While Scala has null (for Java compatibility), Scala encourages the use of Option instead to clearly indicate in the type system whether a value can be missing.
In a web app, this was often used to check whether data store lookups returned anything. And in most cases I wanted to show an error page if not, accomplished in Play by returning a special object from the controller method.
While I’ve read posts such as this one, I couldn’t find a great pattern for using Option in this context. It was leaking up the call stack like a checked exception (code using an Option value had to return another Option value and all the way up). I found myself wanting to do:
val foo = maybeFoo.getOrThrow(WebErrorPageException(“whatever”))
then I wanted the framework to globally catch that (unchecked) exception and show an error page. For all I know Play has such an exception. You can unconditionally Option.get, but that will turn up as an internal server error, which isn’t nice.
Without using exceptions, Option tended to produce nested if() (or pattern match) goo just like null, or creep up into calling functions like a checked exception.
Play itself is very polished, but Play with Scala is not.
Play’s big win, which you get with Scala too, is to bring “just press reload” development to the JVM. You change either code or templates, then go to your browser and test. There’s no pressing build or even waiting for Eclipse to build, and there’s no creating a war file and deploying it.
Play brings other niceties, using convention rather than configuration (in the Rails style). Its template engine is much nicer than JSP/JSF/etc. ugliness, and you get quick and easy mapping from URLs to controller methods. If you’ve used Rails or Django or whatever Play will feel pretty normal. But this normal is a prettier picture than the traditional Tomcat/JSP/blah setup.
Originally, Play was for Java only. The Scala support is marked experimental, and I hit some issues such as:
- The docs are spotty and sort of outdated.
- The docs talk about using JPA (Hibernate) with Scala, but Googling around I learned it’s broken and not recommended. Some of Play’s magic convenience is lost if you aren’t using Play’s database and ORM. I started out trying to use JPA with SQL, but when I learned I’d be manually doing DB stuff anyhow, I switched to MongoDB.
- Some bug in the MongoDB driver makes it break if you also use Joda Time.
- The template engine sometimes couldn’t find object fields from superclasses (my guess, I haven’t tested yet, is that adding @BeanProperty would work around this).
- File upload support only works via a temporary file (a File object) and not streaming.
- Some hacky-feeling conversion from Scala to Java collections/iterators was sometimes needed for the benefit of the template engine.
Little bugs aside, I did get a simple app working and I enjoyed it a lot more than writing in Java.
The stack isn’t nonblocking by default as node.js is.
Play supports suspending an HTTP request and returning to it later, without tying up a thread. Scala, especially in combination with Akka, offers some nice primitives for concurrency while avoiding shared state.
node.js gets huge mileage because the core platform defines what the “main loop” looks like and has primitives for a nonblocking file descriptor watch, nonblocking timeout, and so on. All libraries and modules then work within this framework.
In theory, Play and Akka would let you do the same thing, but since it isn’t the default, you’re going to suffer. You would have to manually write little stub controller methods that always suspended the request and forwarded it to an actor. And your persistence layer probably has a blocking API (such as MongoDB’s blocking API) that would need another bunch of glue code and a thread or actor pool to encapsulate. It’s even an issue, though a simple one, that Akka doesn’t come with Play or Scala, and the Play module for it seems to be well behind the latest Akka version.
I suspect that in a real-world project, you’d start finding blocking IO all over the place, hidden in every third-party jar you tried to use.
It would be great if I could write an actor, annotate it as a controller, and it would receive web request messages and send back web reply messages. Similarly, it would be great if there were a MongoDB-via-actors kind of nonblocking API.
Play’s philosophy is that most controller methods should block because they should be fast; this is likely true, but it’d be nice if the framework overengineered it for me. BTW, here’s a comparison of Play and Node.js.
(I can’t decide whether I like the idea of this Play/Scala/JVM stack or node.js more. I think I like both. The simplicity of node.js, the nonblockingness, and the same language from client to server, make it appealing. But sometimes I like the idea of JVM solidity, the full-featured statically-typed loveliness of Scala, and the giant ecosystem of Java tools and libraries.)
Eclipse support isn’t great yet.
I’ve heard the Scala plugin for Eclipse was recently improved, and honestly I’m not sure whether I have the important improvements in the version I’m using. But it was not great.
- I had an error “class scala.annotation.implicitNotFound not found” that just wouldn’t go away, though it seemed harmless. Suspect that it’s some version mismatch.
- Perhaps because Scala is less verbose and redundant than Java, it’s much easier to get the editor confused about your code.
- Autocomplete/intellisense/whatever-you-call-it rarely worked, the IDE didn’t seem to know what methods my variables had most of the time.
- Refactorings and code generation shortcuts you’d be used to from Java weren’t there (though they were also far less necessary).
All this said, with Play and Scala you’d be fine with an editor instead of an IDE. Working autocomplete would be nice, though.
MongoDB is the Right Thing.
I love MongoDB (disclaimer: I haven’t deployed it, only coded to it). From a development perspective, it’s exactly what I want.
If the data store’s native representation matches the form I really want my data in, then it should (at least eventually) be able to store and query that data intelligently. JPA/Hibernate is a very, very leaky abstraction. I don’t need part of the storage engine inside my app, trying to convert what I really want into SQL. Then the SQL engine has to optimize without high-level information.
As far as I know, MongoDB is the “NoSQL” thing that adapts to how my app works, instead of vice versa. Nice.
There’s a lot of promise here, but if I were building a real app on this stack, I’d be tempted to hack on the stack itself a fair bit (always dangerous!). Play defaults to Java+SQL, and Scala+MongoDB isn’t the well-traveled path.
Disclaimer: I’ve done a lot of Java in the old school Tomcat/JSP/Hibernate style, but Scala, Play, and MongoDB are new to me. Your corrections and elaborations in the comments are very welcome.