Metaphysical Developer

ClojureScript vs Coffeescript

Posted in hacking, Languages by Daniel Ribeiro on August 28, 2011

A language that doesn’t affect the way you think about programming, is not worth knowing

Alan J. Perlis

Edit Feb/2014: Please note that this post is from 2011, a few weeks after Clojurescript was released. Things changed a lot in the mean time…

In the past few years Javascript has gained a lot of attention and ubiquity: HTML5 technologies leverage a lot of Javascript, which enables people to create amazing dream worlds (like the in the ROME project) with WebGL, V8 brings a lot of JIT techniques to a JavaScript Virtual Machine which helps Google Chrome be a very fast browser, and powers NodeJS (allowing people to create a web page in a single programming language).

Javascript also powers queries in NoSQL databases like Mongo and CouchDB, and it can be used when making QT applications, 3d Games in Unity and even mobile apps with frameworks like WebMynd and PhoneGap. It has been a long way from the old days when it was confined to the browser, and mostly used for form validation.

In spite of all of this attention, Javascript has been so misunderstood that attention to its Good Parts had to drawn. It doesn’t help that its prototype based OO was first introduced by the rather unknown language Self, despite the several advantages it has when compared to traditional class based OO (the paper Organizing Programs Without Classes, written by Google’s Senior VP of Operations Urs Hölzle, who, among other things, also contributed to key JIT techniques like polymorphic inline caching).

Therefore it is not surprising that there are many projects that compile existing languages to Javascript. Many of these were too focused on the web platform (like Google Web Toolkit and the amazing Cappuccino‘s Objective-j). On more recent years languages are targeting the whole JS ecosystem (which makes a very poignant argument that JavaScript is Assembly Language for the Web). Coffeescript is one of such languages, which very fond (I’ve written about it recently).

About a month ago ClojureScript was released, porting the Clojure language from Java ecosystem to the JS. Clojure is quite an amazing effort of engineering, not only for being a very successful Lisp on the JVM, but also for its novel approach of handling time and state (which its creator, Rich Hickey, explains really well).

I was really excited to see the examples, but I was a bit bummed out that the most interesting example was a Twitter visualization tool (which feels a bit too much like a 2010 app). Since both CoffeeScript and Clojure are fun languages, and I wanted to see how ClojureScript would compare to CoffeeScript, I took the challenge and crafted a simple game HTML5 physics based game on both languages, using Box2dWeb, a js port of Box2D (the physics engine, created by Erin Catto, that is behind Angry birds).

The game consists of clicking on the objects to destroy them, so that they don’t reach the top of the canvas (and, in another very Tetris like fashion, the elements pop out faster the more you play). It really sticks to the bare minimum of Terrano’s Hierarchy of Gamer Needs. All the code is open source and can be found on Github. The CoffeeScript‘s version source can be found here, and the ClojureScript‘s here.

Lessons Learned

Disclaimer: ClojureScript is pretty much in alpha status, so many things are likely to improve in the future.

Compiling: The first thing that really pops up is how fast Coffeescript compiles down to JS. The watch behavior allows you to fire the compilation process and forget it. ClojureScript takes me about 5 seconds to compile a single file. Granted it gives warning about unused/undefined variables, but I’d really prefer it to compile instantly and let the browser tell me this on runtime.

Namespaces: Clojure’s namespace are implemented as global variables, which are shadowed by local variables with the same name. For instance, if you are in a namespace called game, don’t use local variables and arguments named game. This is really important, as ClojureScript will use the global namespace for every single function defined in that namespace, so shadowing it is likely to give all sorts of errors.

ClojureScript is not Clojure: Fogus wrote an interesting piece on the lack of eval on ClojureScript. Even though I find it might make sense for some web pages, when making WebGL games, or even Canvas 2d games, the assets size can easily overshadow then entire library’s size. Which is not a big deal if you use HTML5’s Cache manifest. In the end, it felt very much like the opposite of the Lisp spirit (epitomized by Paul Graham on his Five Questions about Language Design: “Give the Programmer as Much Control as Possible“).

The documentation is quite clear that eval is not supported. What it is not clear, is that this argument against eval permeates many other functions: resolve is not implemented (neither ns-resolve, or the *ns* definition). Without both of them, there is no way to transform a string into a function. For people more used to OO languages, like Javascript, Ruby and Python, this essentially means that ClojureScript doesn’t have any reflection APIs. On the game:

createElement: ->
  randomY = (0.2 + 0.4 * Math.random())*  H / @scale
  randomX = (Math.random() * (W - 50) + 25) / @scale
  type = @objectList[randomInt(@objectList.length)]
  @["create#{type}"] randomX, randomY, Math.random() + 1

The last line of the Coffeescript version uses reflection to get the correct method name (to decide to invoke createTriangle, createCircle or createSquare ). The ClojureScript version had to be translated into:

(defn- create-element [game]
  (let [randomY (/ (* H (+ 0.2 (* 0.4 (rand)))) scale)
        randomX (/ (+ 25 (* (rand) (- W 50))) scale)
        type (rand-nth [:circle :square :triangle])
        method (keyword (str "create-" (name type)))]
    ((@game method) game randomX randomY (inc (rand)))
    )
  )

Which is possible because the game has the respective functions as keyword attributes, which can be easily converted from string interpolation.

User Macros are not supported: At the moment at least (support for it is likely to come on following updates). This makes the situation above much harder to take. But this is mostly due to ClojureScript’s alpha status. This does make the code a bit longer (the CoffeeScript version has 236 lines, while the ClojureScript has 301 lines). In order to circumvent what I consider that would be one of the ugliest bits caused by lack of macros (several (set! (. obj attr) value) calls), I defined a js-set function:

(defn- js-set
  "Sets an attribute name to a value on a javascript object
Returns the original object"
  ([jsobject attr value]
    (do (native-set-wrapper jsobject attr value)
      jsobject))
  ([jsobject & values]
    (do (doseq [[attr value] (apply hash-map values)]
          (native-set-wrapper jsobject attr value))
      jsobject)))

This is actually really against Clojure’s spirit, as Clojure really promotes immutable code. However Javascript libraries, in particular Box2dWeb, really expect mutable state. Therfore handling native js objects require such functions (note that converting them to clojure and keeping it on clojure land can be easily done with the nice undocumented function js->clj function, which is actually used on TwitterBuz).

Therefore we can write functions this way

(defn- create-fixture
  ([shape] (js-set (b2FixtureDef.)
  :density 3
  :friction 0.3
  :restitution 0.9
  :shape shape
))
([] (create-fixture nil))
)

Instead of:

(defn- create-fixture
  ([shape] (let [f (b2FixtureDef.)]
  (set! (. f density) 3)
  (set! (. f friction) 0.3)
  (set! (. f restitution) 0.9)
  (set! (. f shape) shape)
))
([] (create-fixture nil)))

Which makes it look a lot like assoc function for creating maps with updated values. This version inspired me to refactor the Coffeescript version using a similar assoc function:

createFixture = (shape) ->
f = new b2FixtureDef
f.density = 3.0
f.friction = .3
f.restitution = .9
f.shape = shape if shape?
return f

became:

assoc = (o, i) -> o[k] = v for k, v of i; o

createFixture = (shape) ->
assoc new b2FixtureDef,
density: 3
friction: .3
restitution: .9
shape: shape

Which is quite similar to ClojureScript’s version (the colons are on the right instead of the left, and it requires a comma). Which reduces the amount of accidental complexity to a minimum.

Edit: Thanks everybody for pointing out that you can use macros with Clojurescript. However, at the moment, the are clojure macros (so no js), and they require you hacking your clojurescript to add the macro files in the classpath. Hiccups and cljs-3d are two projects that do this, so you can see on their build files how they do this. Even then, you still need to use require-macros. All of this makes macros less of a native feature on Clojurescript, and it makes a lot harder to share code seamlessly.

IDE support: Clojure’s IDE support is really nice. Intellij’s La Clojure (avaiable on its free Community Version) does a lot more than mere syntax highlight: minimal refactoring support, rainbow parenthesis, smart parenthesis, syntax highlighted repl, great autocomplete support, awesome code navigation, autocomplete for java classes and live templates. And it works pretty well for ClojureScript as well. Other IDEs are also great, even though Emacs support can be a bit more intense on its setup (which is not something emacs users are unfamiliar with).

Even though I am really happy with Github’s founder Chris Wanstrath work on the Emacs mode for Coffeescript, it doesn’t have the same support that Clojure does. It is getting more and more support, but nowadays Clojure has the upper hand.

Debugging support: Browser support for debugging languages that compile down to javascript is coming, but at the moment Cofffeescript compiles down to such a readable JS that it not a big problem. This is a known issue with ClojureScript at the moment. Even on pretty print compile mode.

Conclusions

Since Javascript on the web has a much more simple execution model than Java, Clojure’s amazing concurrency control mechanisms are not as shining. Nevertheless ClojureScript is a delight to work with. As it moves out of its Alpha status, many of the issues are likely to be gone. I also expect it to support the full Clojure language (including things like resolve, letfn, macros and eval), as writing web apps in a single language on client and server is a really nice feature. This would also make ClojureScript even more interesting, as it would allow developers do leverage all the power of existing Clojure libraries into their Javascript work (and possibly use it on a more polyglot environment).

Coffeescript is more suitable for production apps right now, but it is nice to see all these developer efforts to allow people to be more productive and happy with their work on Javascript platforms (this way we don’t have to wait for Google’s NaCl and PNaCL, which promise to bring even more languages to the environment).

Tagged with: , ,

16 Responses

Subscribe to comments with RSS.

  1. Jason Hickner (@jhickner) said, on August 28, 2011 at 5:52 pm

    Nice writeup! I hadn’t heard of rbcoffee before. I’ll definitely be using that in my coffeescript projects. One point – doesn’t ClojureScript support user macros via refer-macros? :

    (ns my.namespace
    (:refer-macros ‘[my.macros :as my])

    • Daniel Ribeiro said, on August 30, 2011 at 9:22 pm

      Thanks. I’ll check the macros. When using them on the same file I get error (and on the repl), which limits clojure compatibility.

      Edit: Edited to include a note on how macros actually work on Clojurescript.

  2. Taariq Lewis (@taariqlewis) said, on August 28, 2011 at 8:56 pm

    Nice piece. Well written. I wasn’t aware we had Clojurescript in alpha. Now, I’ll have to look and explore the options here. Thanks!

  3. Chouser said, on August 29, 2011 at 9:40 am

    ClojureScript does support user macros, though perhaps the documentation is too sparse. Use the :require-macros clause in your ns form to make them available in your namespace.

    Also, your nativejsset fn is already available in ClojureScript as ‘aset’.

    Anyway, thanks for investing your time in writing this demo and post. Using a tool as young as this requires quite a bit of effort, and this write up is helpful. Thanks!

    • Daniel Ribeiro said, on September 21, 2011 at 7:27 pm

      Thanks. Changed the code to use aset instead. From its name it seems that it was meant to set native arrays, but works fine for setting attributes. Using undocumented features is tricky, as they can be dropped/changed without warning, but for the purpose of this experiment, it should be fine.

      Also edited to be more clear on macros. It is important to note that it is not a simple matter of using “require-macros”. But thanks for pointing it out.

  4. Ralph Moritz said, on August 29, 2011 at 10:40 am

    Good article but your link to a “known issue” is actually just someone on the Clojure google group asking if a Closure Inspector mapping file exists for ClojureScript. Clearly not an “issue” as such!

  5. roger said, on August 29, 2011 at 12:35 pm

    shinning -> shining

    • Daniel Ribeiro said, on August 30, 2011 at 1:11 am

      Thanks. Fixxed.

  6. ang21 said, on August 29, 2011 at 10:31 pm

    Hi..How do you feel the code metric…is clojurescript more expressive than coffescrp?…I like clojure but I’m not sure if clojurescript will be a good choice over coffescript or other elections…

    • Daniel Ribeiro said, on September 6, 2011 at 10:35 pm

      Macros are really powerful. The fact that code is just clojure datastructures (since clojure is homoiconic), makes its metaprogramming really powerful. Coffeescript executable bodies for classes can pretty much do a lot of it, but if you use a framework to do Abstract Syntax Tree manipulation (which is what Lisp macros do), you will find it much harder.

      The line metric is not really meaningful without the macros.

  7. Josh T said, on September 13, 2011 at 6:06 pm

    There’s at least one other compiles-to-javascript language to pay attention to: Whalesong, which compiles Racket (née PLT Scheme) into JS. The main page is at http://hashcollision.org/whalesong/, and the source is available on GitHub.

    In case the reader doesn’t know much about Racket:
    I can’t speak on whether Racket’s worth learning if you already know a different Lisp dialect well, but it’s probably the best Lisp to learn if you haven’t yet invested your time into one. For some more detail, I think http://news.ycombinator.com/item?id=1222423 is suitably enthusiastic.

    Anyway, the breadth & depth of the PLT community lead me to guess that Whalesong will be interesting at least.

  8. ylluminate said, on April 26, 2012 at 1:27 pm

    Very nice piece. Thanks for this information. Could you give us an update as to the state of one vs the other as of now? Would very much like to see, from your perspective, of how things have changed or improved.

  9. ylluminate said, on April 26, 2012 at 1:28 pm

    Very nice writeup. Would very much like to see an update to this article to show any changes or reevaluation of state over the last 9 months or so.

    • Daniel Ribeiro said, on April 27, 2012 at 3:19 pm

      Glad you liked it. I’ll see if I do a new post re-evaluating all of it in 3 months, to celebrate 1 year of clojurescript.

      • ylluminate said, on May 5, 2012 at 3:05 pm

        Really look forward to seeing this. I’ve been very impressed with where Node is going, especially with Tower right now, but it would be truly interesting to see a full stack clojure development environment from server to client when it’s really ready for prime time.

  10. masukomi (@masukomi) said, on May 31, 2013 at 6:05 pm

    I should mention Lighttable http://www.lighttable.com/ as an editor / ide for ClojureScript. I believe it was designed with Clojure first, and then expanded to support ClojureScript, and now other languages. It’s also got some amazing interactive functionality.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: