Clojure: Living Without the For Loop (2)

Part 1 one this series introduced Clojure programming beyond simple one-liners and expressions. We were setting up our environment and had a simple demonstration that illustrated the basics. We are using Quil, an implementation of Processing for Clojure, which is a great example of application of program flow that we want to transform into functional code. Our guideline is Matt Pearson’s book ‘Generative Art‘ with its inspirational graphics and code. So let’s get started and try to implement something more complex than we did last time.

This is what we want to draw:

quil-genart-2.1

quil-genart-2.1

I agree, this is not overly impressive. Not yet. But before we’re going into the more spectacular moves we have to learn the basic steps. To draw a growing circle you would write this Processing code:

The basic principle Matt Pearson wants to show here is that you’re setting up things in a setup() function and that the drawing takes place in the draw() function. Draw() is an implicit loop: what’s happening inside draw() is executed again and again. While the various commands in setup() are rather straightforward, the interesting detail in draw() is the exit condition: no circle is drawn when the diam variable exceeds a value of 400.

When you have a look at the official Clojure version written by the authors of Quil you see a one-to-one implementation which even respects the use of the global variables centX, centY, and diam. This is implemented by using the Quil-only method set-state! which, in this case, corresponds to the aforementioned global variables. I wouldn’t recommend this technique in such a short example. Instead, I’ve come up with this Clojure code:

First, we are omitting the draw() method at this time. This frees us from working with global variables or sketch state, because now everything is taking place in setup(). This is perfectly fine, because we are not wanting an endless loop at all: the whole drawing is finished after 40 steps, that’s the distance between 10 and 400 in steps of 10. So there really is no need for set-state!

The -main function and defsketch macro should be familiar to you from part 1 of this series: -main is the entry point, defsketch prepares the window frame. The first few lines in setup are almost like their Processing counterparts, only the variable assignments for cent-x and cent-y have moved into the following let block. Within this block we now have a loop that we are calling back using recur, incrementing the diam parameter by 10. The when condition tests for the value of diam; if it is smaller than 400, a circle of diameter diam is drawn.

This is today’s lesson’s insight: we could have been using a sketch-specific state using set-state! for simulating the global variables of the original Processing code, but we didn’t. We could have used the draw() function, but it wasn’t necessary in this peculiar case. Much more interesting is to understand what the original code actually does: execute drawing circles until a certain condition is met. Using Clojure’s loop and recur combined with a test condition checked with when exactly does that job.

The following parts of this series will further exercise this deconstruction of imperative code. The goal is not to slavishly imitate the original code but to find functional code that will produce the same result. Stay tuned.

About Manfred Berndtgen

Manfred Berndtgen, maintainer of this site, is a part-time researcher with enough spare time for doing useless things and sharing them with the rest of the world. His main photographic subjects are made of plants or stones, and since he's learning Haskell everything seems functional to him.