My first Clojure project
Posted on January 28th, 2015
As part of a game I’m working on with a friend from college, we needed a server that could receive and send messages to multiple players taking part of one shared game. My very first idea was to go with Rails as it is my default framework of choice to start a quick web project with. However, Rails is single threaded and, although there are web servers that will allow you to start up multiple instances of your Rails application to serve multiple concurrent requests, those Rails instances and requests are independent from one another. A consequence for such is that keeping a stream open between client and server consumes an instance in the server side. In our case, that could mean a lot of memory and overhead for a stream that really had very little to do with the logic in the Rails application itself.
As a result, I decided to split the rails project which will serve web pages and manage users, login and a web service in clojure. The reasoning was that the JVM (Java Virtual Machine) world has been dealing with concurrency for a long time and their web servers are quite performant. The JVM itself is also known for on-the-fly optimizations that make it many times as fast as any other language. Given that, the problem had a very functional feel: given the state of the world and a set of commands from multiple players, determine the next state of the world and stream that to each client. Most used/known current functional languages that run on the JVM are essentially Clojure and Scala. I have previously taken the Functional Programming Principles in Scala course by Martin Odersky on Coursera and wasn’t much of a fan of the Scala syntax and integration with Java. Having heard a lot of good things about Clojure, I decided to give it a try.
Being a LISP, I expected Clojure to be parenthesis-intensive and prefix based. I was not disappointed. The platform benefits from the many libraries created for the JVM but the most notable clojure one being Leiningen (or Lein). It is the dependency management, project configuration and task automation tool for Clojure. It’s fairly simple to use basing itself mostly only on a project.clj file. Having that file, you can easily add multiple dependencies as well as lein plugins to enhance your project as well as the options lein provides you.
By using a few things I managed to get the following:
- Run tests as soon as my files changes (using lein-test-refresh plugin and just “lein test-refresh").
- Compile all my clj files into a single JAR that be used standalone (thanks to lein itself with “lein uberjar").
- Provide me with coverage information thanks to lein-cloverage with just “lein cloverage".
- Provide me with dependency tracking (thanks to jarkeeper).
- Provide me with a running server that reloads my changes in development immediately with “DEV=true lein run" (using http-kit and composure).
- Provide me with a Continuous Delivery (actually Continuous Deployment) thanks to Snap-CI and Heroku.
My workflow goes about the usual:
- Start my test watcher with “lein test-refresh" and my server with “DEV=true lein run"
- Write a test that fails (and get a nice notification about it)
- Write the code to pass the test (and get a nice notification about it)
- Repeat that until I’m happy.
- Try the result out manually with a browser and JS (to use web sockets)
- Commit and push
- Receive a nice RSS feed from Snap-CI telling me all went fine and the result is available at Heroku.
Overall, so far, I’m quite pleased with the experience except for a few things:
- The lack of structure in building types caught me a little by surprise. Having had most of my functional programming experience in Haskell, strong static typing helped me ensure correctness of my data and code. Clojure seems to be a lot more into building your data structures just using lists and maps. When I want to change that structure, it is a lot more annoying to find all the places that are impacted by it. A few friends recommended schema to ensure the maps/lists are as expected. Haven’t tried yet.
- Automated testing libraries are not as many but all of it is pretty obscure when it come to which is recommended. The default one is clojure-test which feels just like another xUnit library. Brian Marick started midje which feels like a mix between BDD, TDD and some mathematical view of functional programming. speclj is a fully BDD like library. Finally Jay Fields started expectations. When it comes to choosing one, it goes everywhere. Clojure-test is more common because it’s default but it’s also not the most feature heavy one. Midje seems to be very adopted but there aren’t that many obvious benefits to it. Speclj is slightly less adopted but, although it gives the BDD feeling to the way tests read, it doesn’t add all the benefits RSpec does. Finally expectations seems to have a lot of feature but is not very much adopted.
I decided to go with clojure-test on Leonardo Borge’s recommendation. Haven’t regretted it. My tests are fast and simple. Readability is not always great so I need to put in more effort to make my functions more expressive but aside from that, it’s fine. The simple data structure makes it so that pretty much all of my tests are just "(is (= expected actual))" so it’s not that bad.
- Namespacing still isn’t clear to me as a way to ‘hide’/'show’ functions and providing encapsulation. Deep structures also seem to be hard to mimic in that sense. I’m sure this is only my own inexperience as I know there are already at least mid-sized clojure projects out there that have had to deal with it. But, for now, it doesn’t seem the community is addressing that fact a lot.
In the end, I don’t regret my choice. My server is very fast and coding is going well. Immutable state and function composition makes it fairly simple to introduce changes and, aside from dealing with data, the whole thing is pretty smooth.