I apologize for not wrapping up my series of posts on nominal logic programming, I'll return to that bit of fun soon enough. But lets take leave of theoretical computer science and turn to something more "pragmatic".
Without further ado here it is (it will look best in Chrome & Firefox):
applications, unnecessarily wields global mutation - the procedural
texture and block generation both operate on global mutable arrays. In
the ClojureScript port we instead have functions that return the
populated arrays. this is the value oriented approach -
set! is pure code smell for a Clojure programmer.
For example here is the block generation code:
Ok, but still isn't it cheating to be bashing on mutable arrays inside of functions?
No even among people who argue even more strongly for functional purity, local mutation is absolutely ok. Clojure has long supported this insanely great idea in the notion of transients.
But what about
render-minecraft!? In the original Notch allocated
ImageData instance once and he would bash on this at each turn
of the loop.
Surprisingly this bit of optimization is entirely unnecessary. We can
allocate this internally every single time
called - the cost of allocating a 424 by 240
ImageData object is
completely dwarfed by the real work done in
I admit one tricky bit that required experimentation is that Clojure's
semantics don't admit mutable locals - something that Notch's code
uses freely. This required a little bit of experimenting, I tried
Box type with one mutable field, I tried putting the entire
render step into a
deftype with mutable fields. In the end I settled
on representing mutable locals as arrays of one element. The
performance of this representation is stunningly good on Chrome and
pretty good in Firefox as well. Surprisingly Safari performs the least
well on this bit of code and I haven't had time to dig into why.
I honestly spent most of the development
time just trying to understand what the original code did. I did find
the ClojureScript development cycle relatively pleasant due to
auto feature. I wish we had CoffeeScript's
lightning fast build times, but once the JVM is warm, the turn around
is not large enough to matter.
The one real scratch your head issue I ran into while developing this was a Google Closure mishandling of the modulo operator. Closure will incorrectly remove parentheses, this is easily worked around by putting the result of the modulo operation in an intermediate variable but I lost more time on this subtle issue than I care to recall.
I think the intersection of computationally intensive games and functional programming is a rich area to innovate and ClojureScript provides the tools needed to forge new ground.