clojure-logo.png

Introduction to Clojure

Clojure is simple, easy, fun, and very cool. Some have said that Clojure — or a language very much like it — is the future of programming languages.

Overview

Clojure

The current production version is 1.4.

Links

Important and useful information about Clojure itself

Cultural readings

Videos

Getting Started

First, you'll need to install Java 1.5 or above and download the Clojure jar file. Then, as with most languages, you can use Clojure in two ways:

Actually, there is an alternate approach to all this we'll see after you learn how to do things the hard way.

Writing simple scripts

Make a file called hello.clj that looks like this:

hello.clj
(println "Hello, world")

To run this script (assuming the clojure jar is named clojure.jar and is in the current directory — if not, you'll probably figure out what to do):

$ java -cp clojure.jar clojure.main hello.clj
Hello, world

Another simple script:

triple.clj
; This script prints all integer Pythagorean triples for values <= 100.

(doseq [c (range 1 101) b (range 1 c) a (range 1 b)]
  (if (= (+ (* b b) (* a a)) (* c c))
    (printf "(%d,%d,%d)\n" a b c)))
$ java -cp clojure.jar clojure.main triple.clj
(3,4,5)
(6,8,10)
(5,12,13)
(9,12,15)
.
.
.
(65,72,97)
(60,80,100)
(28,96,100)

Another simple script, this time using commandline arguments:

gcd.clj
; This script prints the gcd of its two command line arguments

(defn gcd [x y]
  (if (= y 0) x (gcd y (mod x y))))

(println
  (gcd
    (read-string (first *command-line-args*))
    (read-string (second *command-line-args*))))
$ java -cp clojure.jar clojure.main gcd.clj 42 96
12

Using interactive mode

$ java -cp clojure.jar clojure.main
$ user=> (+ 8 7)
15
$ user=> (/ 8 7)
8/7
$ user=> (type (/ 8 7))
clojure.lang.Ratio

Now try to hit the up arrow key to bring back the last command. Ooops. Or type a line and try using the right and left arrow keys. Pretty annoying, huh?

Leiningen

You'll want to use Leiningen to do anything with Clojure. Follow the instructions on its main project page on github for installation. Now you can fire off a repl using the lein script.

$ lein repl
user=> (/ 22 7.0)
3.142857142857143
user=> (* (/ 3 4) (/ 2 5))
3/10
user=> (defn fact [a] (if (<= a 1) 1 (* (fact (- a 1)) a)))
#'user/fact
user=> (fact 40)
ArithmeticException integer overflow  clojure.lang.Numbers.throwIntOverflow
(Numbers.java:1388)
user=> (defn fact [a] (if (<= a 1N) 1N (* (fact (- a 1N)) a)))
#'user/fact
user=> (fact 40)
815915283247897734345611269596115894272000000000
user=> (type \k)
java.lang.Character
user=> (type :k)
clojure.lang.Keyword
user=> (type 'k)
clojure.lang.Symbol

Learning By Example

Here are few example evaluations in the REPL to get you warmed up. After you check these examples out, please go spend some time with the Cheatsheet and the Core QuickRef.

Symbols

user=> (type 'k)
clojure.lang.Symbol
user=> (type k)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: k in
this context, compiling:(NO_SOURCE_PATH:1)

That's because things are evaluated: the value of `k is the symbol k, but when we just write k we mean the value associated with k. Confused yet? Okay this may help:

user=> (def k 5)
#'user/k
user=> 'k
k
user=> k
5

Numbers

You'll recognize some of the numeric types from Java, but Clojure does add some new ones.

user=> (type 9)
java.lang.Long
user=> (type 9223372036854775807)
java.lang.Long
user=> (type 9223372036854775808)
clojure.lang.BigInt
user=> (type 579347593875938474772983477234283929398762781091283)
clojure.lang.BigInt
user=> (type 7N)
clojure.lang.BigInt
user=> 2.35E234
2.35E234
user=> (type 2.35E234)
java.lang.Double
user=> 2.35E999
Double/POSITIVE_INFINITY
user=> 2.35E999M
2.35E+999M
user=> (type 2.35E999M)
java.math.BigDecimal

Strings

user=> (str "94" 2 6 " dogs")
"9426 dogs"
user=> (format "It was %d %s" 40 "degrees")
"It was 40 degrees"
user=> (count "привет")
6
user=> (subs "Hello" 2 4)
"ll"
user=> (subs "I'm hungry" 4 7)
"hun"
user=> (split "192.168.1.1" #"\.")
CompilerException java.lang.RuntimeException: Unable to resolve symbol: split in this
context, compiling:(NO_SOURCE_PATH:1)
user=> (clojure.string/split "192.168.1.1" #"\.")
["192" "168" "1" "1"]
user=> (use 'clojure.string)
nil
user=> (split "192.168.1.1" #"\.")
["192" "168" "1" "1"]

Collections

Clojure has four basic collection types:

Clojure collections are immutable and persistent. Adding or removing produces a new object, rather than mutating the collection. They use structural sharing, are efficient, and inherently thread-safe.

The following functions work on all collection types:

Lists

A Lists is a sequence in which count is Θ(1) and conj adds to the front.

Lists are immutable, of course:

user=> (def x (list 1 2 3 4 5))
#'user/x
user=> x
(1 2 3 4 5)
user=> (conj x 100)
(100 1 2 3 4 5)
user=> x
(1 2 3 4 5)

Please make a point to fully understand first, rest, and cons. They are part of Clojure's Lisp heritage (actually List uses the terms car, cdr, and cons, but that's just life).

user=> (def dogs (list 'sparky 'spot 'spike 'rex))
user=> (first dogs)
sparky
user=> (rest dogs)
(spot spike rex)
user=> (cons 'max dogs)
(max sparky spot spike rex)
user=> dogs
(sparky spot spike rex)

Vectors

A Vector is a collection of values indexed by contiguous integers. The conj operation adds to the end.

user=> (def dogs ['sparky 'spot 'spike 'max])
user=> (type dogs)
clojure.lang.PersistentVector
user=> (count dogs)
4
user=> (conj dogs 'rex)
[sparky spot spike max rex]
user=> (peek dogs)
max
user=> (get dogs 2)
spike

Sets

A Set is a collection of unique elements.

user=> (def dogs #{'sparky 'spot 'spike 'max})
user=> dogs
#{sparky max spot spike}
user=> (= dogs #{'spike 'max 'spot 'sparky})
true
user=> (conj dogs 'rex)
#{rex sparky max spot spike}
user=> (count dogs)
4
user=> (disj dogs 'sparky)
#{max spot spike}
user=> (contains? dogs 'spot)
true
user=> (contains? dogs 'sport)
false

There are hashed and sorted varieties of sets:

user=> (vec (hash-set 5 8 10 2 1 6 -5))
[1 2 -5 5 6 8 10]
user=> (vec (sorted-set 5 8 10 2 1 6 -5))
[-5 1 2 5 6 8 10]

Maps

A Map is a collection mapping keys to values.

user=> (def capitals {:ca "sacramento" :hi "honolulu" :ak "juneau"})
#'user/capitals
user=> (type capitals)
clojure.lang.PersistentArrayMap
user=> capitals
{:ca "sacramento", :hi "honolulu", :ak "juneau"}
user=> (capitals :ca)
"sacramento"
user=> (count capitals)
3
user=> (keys capitals)
(:ca :hi :ak)
user=> (type (keys capitals))
clojure.lang.APersistentMap$KeySeq

The assoc function returns a new map just like the original one except with one of the fields updated.

user=> (def dueDate {:month 12 :day 16 :year 2015})
#'user/dueDate
user=> dueDate
{:month 12, :day 16, :year 2015}
user=> (assoc dueDate :day 26)
{:month 12, :day 26, :year 2015}
user=> dueDate
{:month 12, :day 16, :year 2015}
user=> (def lateDate (assoc dueDate :day 26))
#'user/lateDate
Exercise: Learn how to create maps with hash-map, array-map, and sorted-map, and research the difference between these three kinds of maps.

Defining Functions

Function objects are introduced with fn:

user=> ((fn [x] (+ x x x)) 20)
60

Like any other value, you can bind a function to a name. This lets you call it through the name:

user=> (def triple (fn [x] (+ x x x)))
#'user/triple
user=> (triple 2)
6

As a convenience you can use defn:

user=> (defn triple [x] (+ x x x))
#'user/triple
user=> (triple 5)
15

There's even a shorter form, #(...) where parameters are called %1, %2, %3 and so one. (You can even use % for %1.)

user=> (def triple #(+ % % %))
#'user/triple
user=> (triple 4)
12

The short form is very convenient for higher-order functions:

user=> (map #(* 3 %) '(1 2 3 4 5 6))
(3 6 9 12 15 18)
user=> (reduce #(+ (* 2 %1) %2) '(1 2 3 4 5 6))
120

Local Variables

user=> (defn energy [m] (let [c 299792458] (* m c c)))
#'user/energy
user=> (energy 75)
6740663840526132300
user=> (defn bmi [pounds inches]
  (let
    [KILOGRAMS_PER_POUND 0.45359237
     METERS_PER_INCH 0.0254
     kilos (* pounds KILOGRAMS_PER_POUND)
     meters (* inches METERS_PER_INCH)
    ]
    (/ kilos (* meters meters))))
#'user/bmi
user=> (bmi 155 69)
22.88926377737234

Local Functions

user=> (letfn [
  #_=>   (sum [x y] (+ x y))
  #_=>   (half [x] (/ x 2))
  #_=> ]
  #_=> (half (sum 100 200)))
150

Structs

Structs are still around, but you should be using records instead.

user=> (defstruct point :x :y)
#'user/point
user=> (struct point 5 8)
{:x 5, :y 8}
user=> (defn makePoint
  ([] (struct point 0 0))
  ([x y] (struct point x y)))
#'user/makePoint
user=> (def p (makePoint 6 8))
#'user/p
user=> (p :x)
6
user=> (:x p)
6

Cool Stuff

Protocols and Datatypes

A great introduction: Solving the Expression Problem

From the Clojure docs:

Iteration and Recursion

As a functional programming language, Clojure wants you to avoid mutable state as much as possible. How do you do this for simple loops, such as computing a factorial? You should not be creating a variables! THAT WOULD BE JUST SO WRONG.

But the classic recursive algorithm is not good!

; This isn't good!
(defn fact [n] (if (<= n 1) 1 (* (fact (- n 1)) n)))

It is bad because each recursive calls fills up the call stack with unevaluated forms:

fact 4
= 4 * fact 3
= 4 * (3 * fact 2)
= 4 * (3 * (2 * fact 1))
= 4 * (3 * (2 * (1 * fact 0)))
= 4 * (3 * (2 * (1 * 1)))
= 4 * (3 * (2 * 1))
= 4 * 3 * 2
= 4 * 6
= 24

It would be better to create a tail-recursive formulation. Let's make a little function called f that takes in two arguments, and use it as a helper. Like this:

; This is tail recursive so you would think it's good....
(defn fact [n]
  (letfn
    [
      (f [i a] (if (<= i 1) a (f (dec i) (* i a))))
    ]
  (f n 1)))

How does this work? Let's just do the evaluation:

fact 8
= f 8 1
= f 7 8
= f 6 56
= f 5 336
= f 4 1680
= f 3 6720
= f 2 20160
= f 1 40320
= f 40320

That is a neat little function! Notice that we are not mutating the variable i; we're making recursive calls. The binding of the argument to the parameter gives it the feel of updating i and a, but we're not really. FYI, the second variable a stands for "accumuator" because it is building up (accumulating) the result.

This function is tail recursive because it returns the result of a recursive call. So a good compiler and a good runtime system can evaluate this function as if it were a loop! Alas, the JVM cannot do this. So Clojure has loop and recur, to play the role of the inner function. Check this out:

(defn fact [n]
  (loop [i n a 1]
    (if (<= i 1) a (recur (dec i) (* i a)))))

Here the inner recursive helper is replaced with a loop expression that initializes i to n and a to 1, then appears to "call itself," but it's really just repeating the "loop body" with updated values.

Keep in mind, however, that Clojure has lazy sequences and a wealth of powerful sequence operators. So you might actually do factorial this way:

defn factorial [n]
  (reduce * (range 1 (inc n))))

or even

(defn factorial [n]
  (apply * (range 1 (inc n))))

Sequences

From the documentation:

A seq is a logical list. ... Seqs differ from iterators in that they are persistent and immutable, not stateful cursors into a collection. As such, they are useful for much more than foreach - functions can consume and produce seqs, they are thread safe, they can share structure etc. ...

Most of the sequence library functions are lazy, i.e. functions that return seqs do so incrementally, as they are consumed.

Many of the functions in the seq library take one or more collections, call seq on them, and then operate on the resulting seq. In other words, many of these functions take collections but operate on their seqs.

Please see the the Clojure documentation page on sequences.

Transients

Transients can speed up certain operations a lot. Read about them.

Macros

Ah, macros.... Macros are what makes a Lisp Lisp. They are so cool we need to have a whole page describing them.

Basically a macro looks like a function but the arguments are not evaluated when the macro is called; they are passed to the macro as unevaluated forms. Then the macro may or may not evaluate them.

Read about them.

Interoperating with Java

See the Java interop page at clojure.org for details.

Working with Clojure

Unit Tests

physics.clj
(ns physics)

(defn energy [m]
  (let [c 299792458] (* m c c)))
physicstest.clj
(use 'physics)
(use 'clojure.test)

(deftest energy-test
  (is (= (energy 0) 0))
  (is (= (energy 1) 89875517873681764))
  (is (= (energy (/ 1 4)) 22468879468420441)))

(run-tests)

Make sure to put the current directory (or whatever directory the source code is in) on your classpath.

$ java -cp clojure.jar:. clojure.main physicstest.clj

Or better, test with Leiningen.

Building Projects

Use Leiningen to set up, build, and deploy Clojure projects in much the same way you use Maven to manage your Java projects.

Documenting

When defining functions, you can place a doc-string between the function name and the parameter vector, then refer to it later with doc, or use a documentation generator.

user=> (defn square "squares a number" [n] (* n n))
#'user/square
user=> (square 8)
64
user=> (doc square)
-------------------------
user/square
([n])
  squares a number
nil

Libraries

The libraries that are included with Clojure include:

clojure.data, clojure.inspector, clojure.java.browse, clojure.java.browse-ui, clojure.java.io, clojure.java.javadoc, clojure.java.shell, clojure.main, clojure.pprint, clojure.reflect, clojure.repl, clojure.set, clojure.stacktrace, clojure.string, clojure.template, clojure.test, clojure.test.junit, clojure.test.tap, clojure.walk, clojure.xml, clojure.zip.

These libraries are actually namespaces. In this sense, clojure.core is a library too. Detailed information on these built-in libraries (namespaces) is at the Clojure API Overview page or at ClojureDocs. Also check out the built-in library documentation at clojure.org.

There are also a large number of contributed libraries whose names begin with clojure.contrib.

Find information on the contributed libraries ClojureDocs's contrib page.

More

The Clojure Beginner's Guide by John Gabriele has a number of very useful links (including videos) where you can get more information — as well as help from the Clojure community.