July 14, 2015

Quil, Clojure, and WORA

Introduction

Since my last post, I've been playing with the excellent Quil library, a "Clojure/ClojureScript library for creating interactive drawings and animations." Prior to this, I was writing demonstrations using the following scheme:

  1. Develop all logic using cljc files (cljx before Clojure 1.7). In general, application logic is pretty platform-independent and can be wholly or nearly-wholly written in whatever language you are working in (Clojure in our case). Any slight variances (e.g. having to create a java.util.Date or a JavaScript Date) can easily be handled by Clojure's new Reader Conditionals.
  2. Decide on a UI technology (likely Java Swing with Java2D or HTML Canvas depending on what type of demo I wanted) and implement a front end using that particular choice.
  3. Optionally implement the "other" solution from #2 so that I now have a Java and JavaScript solution. This strategy works pretty well, but I still have duplication of effort when it comes to the user experience. The awesome thing about Quil is that it allows you to launch your UI as either a Clojure or ClojureScript application targeting the JVM or JavaScript-enabled browser, respectively. Now, I can pretty much write everything as cljc files with a few reader conditionals and easily produce targets that run on the JVM or in a browser.

To get my feet wet with Quil, I re-implemented the renderer for my Lunar Lander project in Quil. I was so happy with the results that I removed the Canvas/ClojureScript renderer completely and now just use a single renderer for both the JVM and JS versions of the project.

Results

By the time I was done with my new rendering code and refactoring of everything else, all of the cljs files were gone. I now have a single clj file that is nothing more than a (:gen-class) macro and a main function calling the application entry point in the cljc code. Everything else is written as cljc files. Only a small number of reader conditionals were used to make any Clojure or ClojureScript specific changes as required. Rather than go over every example of how I used reader conditionals, take a look at this file that demonstrates how to create a single Quil "Sketch" that works with both host platforms. Aside from the above linked file, the only other conditionals required were a couple java.util.Date vs. JavaScript Date conditionals used in the simulation namespace. The vast majority of the code was identical for all hosts.

Conclusion

I've been developing code for the JVM for over 10 years and have always liked the "Write once, run anywhere" (WORA) aspect of JVM languages, and have found that WORA works for the most part. However, you can't always rely on your host machine having a modern JVM, especially in the era of mobile devices. The browser is really the most ubiquitous host platform, and the ability to write a single application that can be compiled to run as a JVM app or a JavaScript app with very little effort is a huge advantage for Clojure and Clojurists.

Afterthoughts

The complete application described in this blog can be found here. Note that you will need to build and install my numerics project as well. Once numerics is installed, cd over to lander and type lein run for the Clojure version of the game. You can play the web version right here (Note that you will need a keyboard or some way to emulate the 'f' key and left and right arrow keys.).

Note that due to the way markdown munges underscores I needed to update the js file used for this demo. The new file includes some additional features such as a fuel budget. Good luck!

Tags: quil clojure functional programming clojurescript