Learning Clojure - Introduction
Ever since I first saw the SICP-Lectures held by Harold Abelson and Gerald Sussman I wanted to spend some time playing with functional programming in Lisp. However - learning the syntactic and paradigmatic particularities plus the API of a new language like Common Lisp, Scheme or Emacs Lisp is a pretty high hurdle to get your weaker self around.
Fortunately there is a new star in functional programming heaven: Clojure, a lisp-dialect invented by Rich Hickey. Clojure offers some nice features that significantly lower the barrier of entry compared to other lisps.
Syntactic Sugar
It's a little less asketic in the syntactic department compared with other lisp dialects. When it comes to first class datastructures, traditional Lisps usually limit themselves to pure lists.
(1 2 3 4 5)
In Clojure there's also syntactic support for near-constant-time-lookup vectors
[1 2 3 4 5]
and associative arrays (hashtables).
{ :first-name "Rich" :last-name "Hickey" }
A JVM Language
Clojure runs on the Java Virtual Machine and interoperates painlessly with Java. The API provided with the language is very lean and covers little more than sequences. Using your existing Java code is not only possible, it's encouraged. To give a quick impression about the workings of Java interop, here's a little program writing a welcome message and the current system's separator into a file.
In Java
import java.io.File; import java.io.FileOutputStream; import java.io.StringWriter; public class HelloWorld { public static void main( String[] args ) { try { FileOutputStream stream = new FileOutputStream( "helloworld.txt" ); StringWriter writer = new StringWriter(); writer.write( "Hello World!\n"); writer.write( "The separator on this system is: "); writer.write( File.separator ); writer.write( "\n" ); stream.write( writer.toString().getBytes("UTF-8") ); stream.close(); } catch (Exception e ) {} } }
In Clojure
(let [stream (new java.io.FileOutputStream "helloworld.txt") writer (new java.io.StringWriter)] (.write writer "Hello World!\n") (.write writer "The separator on this system is: ") (.write writer (java.io.File/separator) ) (.write writer "\n") (.write stream (.getBytes (.toString writer) "UTF-8") ) (.close stream) )
Immutability
Clojure's data structures are immutable. You can't just add an element to - say - a vector. You have to create a new instance that contains the element. Now, creating a full copy of a vector just to add an element is an obviously stupid idea. But since no part of the existing vector will ever change, you can just take your element plus the vector and link them together. Hickey invested a lot of effort to implement all of Clojure's own data structures in a way so that you can build larger data structures from smaller ones by linking them together without sacrificing the memory and performance footprint of their mutable cousins.
Now you might ask yourself: "Ugh... great work - but what the hassle for?".
Simple. If a container doesn't ever change, you can safely share it among threads without stepping onto the notorious concurrency landmines.
The Plan
What's left is to come up with a fun task to teach myself Clojure. Something that does heavy computation, needs to build upon existing code, can be parallelized and looks pretty - let's see. How about building a raytracer?
A raytracer is a computer program that computes an image by tracing the path of light through pixels in an image plane.
This is a pretty daunting task for a single blog post, so I'm gonna baby-step this into several parts:
-
- Setting up an environment, that lets us compile and run Clojure programs
- Implementing an image structure, that lets us write colors to pixels and save itself to a file
-
- A short introduction into the math behind raytracing
- Implementing the required computational objects
- The phong shading model on a sphere
-
Raytracing Basics 2
- Shadows
- Reflection
-
Exploring Clojure's polymorphism by introducing some new geometric primitives
- Planes
- Cubes
-
Benchmarking Clojure against some other JVM-languages by porting some key computations
- Rhino
- JRuby
- Scala (maybe?)
- pure Java
I'll post a new chapter every once in a while, so stay tuned...
Leave a comment: