Monday, October 20, 2008

Lisp and Clojure

Clojure is an implementation of Lisp on the JVM.   Lisp derives its name from 'List Processing'.   Clojure uses Lisp's syntax but uses Java's data structures, lists, vectors, sets and maps.   Because Clojure can runs on the JVM, it can interact with your Java classes too.  

Why Clojure?

1. How many processors does the machine have? How many does your program use? How many would you like to use?
2. Author stipulates mutable objects are the new spaghetti code.   Concurrency is difficult in Java.   Java allows direct references to mutable objects.   Clojure makes concurrency easier since it allows only indirect references to immutable objects (only references mutate).

Features of Clojure

- Concurrent programming based on immutable data structures
- Software Transactional Memory takes care of the hard parts of concurrent programming
- Meta-programming
- Functional programming
- Object support
- Java interoperability

Clojure implements hairy locking code so you don't have to.   Example - multiple cells on the screen, multiple ants searching the cells for food, only one ant per cell, ants mark cells with pheromones (which evaporate) which lead other ants to the food which they can pick up and take home.   Many simultaneous processes.   Lots of contention.

Documentation

A free download of the 2005 book Practical Common Lisp is available at Apress.
The project website.
The wikipedia article
The extremely helpful wikibooks entry. Stuart Halloway's blog is a great reference, too.
High quality video presentations by Clojure's creator are available as iTunes podcasts.

Traditional Lisp

LispWorks provides a Lisp execution environment for Windows and Mac.   LispWorks provides the HyperSpec.   LispWorks does not use Clojure or easily interoperate with Java.   (new java.util.Date) works in Clojure but not in LispWorks.   You can see how much better Clojure is.

Installing and Running Clojure

The following steps are sufficient to get Clojure running on your system.   The Clojure entry in wikibooks gives additional details.   Subversion access to Clojure's code is documented at http://sourceforge.net/svn/?group_id=137961.   Clojure compiles and runs like any other Java project.   First, download the project.

dev> svn co https://clojure.svn.sourceforge.net/svnroot/clojure clojure

After acquiring the code, change directory to the trunk folder and execute maven.

dev> cd clojure/trunk trunk> mvn install

Now, run it.

trunk> cd target target> java -cp clojure-lang-1.0-SNAPSHOT.jar clojure.lang.Repl

or, if you have jline ( very helpful command line utility),

target> java -cp .:../../lib/jline-0.9.94.jar:clojure-lang-1.0-SNAPSHOT.jar jline.ConsoleRunner clojure.lang.Repl

Comparison to Java

Clojure requires less ceremony to accomplish the same result.   In Clojure, you would define person with a single line:   (defstruct person :first-name :last-name) but, in Java, a class with field getters and setters would be around 20 lines of code.

Examples

List (1 2 3 4 5)

same as (1, 2, 3, 4, 5)

Vector [1 2 3 4 5]

Map {:a 1 :b 2 :c 3 :d 4}

Set #{lisp scheme clojure}

Simple Operations

(drop 2 [1 2 3 4 5]) -> (3 4 5)

(partition 3 [1 2 3 4 5 6]) -> ((1 2) (3 4) (5 6))

(map vector [:a :b :c] [1 2 3]) -> ([:a 1] [:b 2] [:c 3])

(def m {:a 1 :b 2 :c 3}) -> #=(var user/m)

(m :b) -> 2

(keys m) -> (:a :b :c)

Assignment

Clojure is free of side effects, no matter how desirable the side effect.   Your code does not modify your variables.   You must capture the output in a new variable in order to retain the output of the operation. user=> (def sm (sorted-map :a 1 :b 2)) #=(var user/sm) user=> (conj sm {:c 3}) {:a 1, :b 2, :c 3} user=> sm {:a 1, :b 2} user=> (def sm2 (conj sm {:c 3 :d 4})) #=(var user/sm2) user=> sm2 {:a 1, :b 2, :c 3, :d 4}

Java Interoperability

(new java.util.Date) (. Math PI)

(.. System getProperties (get "java.version"))

Thread Safety

Clojure has three types of references vars - isolated, no sharing; store functions in vars; refs - share synchronous changes between threads (transactional) agents - share asynchronous changes between threads

Evaluation

This code shows how code is data.   The quote symbol causes an expression to remain unevaluated.   The eval function will cause the quoted expression to be evaluated. user=> (def x 6) #=(var user/x) user=> x 6 user=> '(def y 7) (def y 7) user=> (eval '(def y 7)) #=(var user/y) user=> y 7 user=>

More on LISP

Before use of the stored program architecture (pioneered by Turing and Von Neumann in the 1930s), computers were either non-programmable or had to be rewired in order to perform different functions.   The stored program architecture stores the program instructions in digital form and executes the instructions eliminating the need to rewire the machine.   LISP uses the stored program concept but code and data are not as differentiated in the way they are in traditional languages.   Simply, LISP allows data to be evaluated as instructions.