Getting Started with Clojure

This series is a practical introduction to functional programming through Clojure, a modern Lisp dialect. The ideas here transfer to other languages, but Clojure makes some of the most powerful concepts—syntax as data, immutable values, explicit control flow, and safe concurrency—first-class parts of the language rather than incidental details.

We'll be deliberately skipping deep dives into static type analysis, hardware, and performance tuning. Those topics matter, but they don't fit the narrative arc of learning to think functionally. If you want type theory, pick up Haskell; if you want to understand the machine, learn C. Clojure itself sits in a sweet spot: fast enough for most work (orders of magnitude faster than Ruby or Python), concise, and designed for safety in concurrent programs. Its REPL and dynamic typing make it approachable for experimentation.

There are tradeoffs worth knowing upfront. Clojure compiles to JVM bytecode, so startup is slower than a scripting language—not ideal for tiny shell scripts. And while numeric code is possible, matching Java's raw performance requires considerable effort. We'll flag these constraints as they come up.

Who This Is For

Software, science, and engineering are deeply rewarding fields, yet too many people are pushed out or discouraged before they start. The culture around tech often assumes its practitioners are white, straight, and male. That assumed default excludes a lot of talented people who are perfectly capable of designing suspension systems, writing spacecraft software, or building distributed databases—if they get the chance and the support.

That's why this guide is free and open to everyone. You don't need to already be a programmer. You don't need to fit any particular identity. What you need is curiosity, persistence, and a willingness to put in the hours. Your gender, background, or what other people assume about you has nothing to do with your ability to learn this material.

Installing the Pieces

Clojure runs on the Java Virtual Machine, so your first step is getting a JDK installed. Windows users can grab Oracle JDK 1.7; OS X and Linux users may already have one. Check in a terminal:

which javac

If you see output like this, you're set:

/usr/bin/javac

No output means no JDK—install one from Oracle or your package manager before continuing.

Next, you need Leiningen, the standard Clojure build tool. It manages your Clojure installation, pulls down libraries from the internet, and handles building and running projects. Linux and OS X users can follow the setup below; Windows users should check the Leiningen site for an installer. If you're new to the command line, a quick primer on terminal basics will help.

mkdir -p ~/bin
cd ~/bin
curl -O https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
chmod a+x lein

Now create a project to experiment in:

cd
lein new scratch

That makes a new directory called scratch in your home folder. If you get command not found, your terminal doesn't know about ~/bin. Add this line to .bash_profile in your home directory, then run source ~/.bash_profile:

export PATH="$PATH":~/bin

After sourcing the profile, lein new scratch should work. Move into the directory and launch the Clojure REPL:

cd scratch
lein repl

With the REPL running, you're ready to start exploring expressions, values, and functions—the fundamental building blocks of Clojure.

From Values to Sentences

When you start a Clojure session with lein repl, you’re dropped into an environment that reads what you type, evaluates it, and prints the result:

aphyr@waterhouse:~/scratch$ lein repl
nREPL server started on port 45413
REPL-y 0.2.0
Clojure 1.5.1
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)

user=>

This read-evaluate-print loop, or REPL, is the workbench for interactive development. It gives you immediate feedback on any expression you feed it, making it ideal for testing ideas and exploring code.

The simplest thing you can type is a value. Enter nil, and Clojure returns it right back:

user=> nil
nil

nil is Clojure’s representation of nothing — the absence of any value.

user=> true
true
user=> false
false

Sitting alongside nil are the Booleans, true and false, which mark whether a statement holds. These three values form the foundation of Clojure’s logical system.

Numbers extend the vocabulary further. Zero, integers, negatives, fractions and decimals all qualify, as do strings — chunks of text wrapped in double quotes:

user=> 0
0
user=> "hi there!"
"hi there!"

These values are the nouns of programming: they state that something exists. But most useful programs involve action. That requires verbs — things that do work on the nouns.

user=> inc
#<core$inc clojure.core$inc@6f7ef41c>

Here, inc is a verb that increments a number. Technically, it’s a symbol pointing to a function, shown as #<core$inc clojure.core$inc@6f7ef41c>. The symbol is a label for a concept, just as the word “run” names the act of running. The ink on the page is not the running itself — it’s a reference that carries meaning when interpreted.

Clojure evaluates a symbol by looking up what it points to. Evaluate inc, and you see that function object. But you can also refer to the symbol itself without triggering that lookup:

user=> 'inc
inc

The single quote ' escapes an expression. Instead of evaluating the text, Clojure returns the text itself, unchanged. Quote a symbol and you get the symbol; quote a number and you get the number. A quote suspends interpretation.

Lists as Sentences

Wrap multiple values in parentheses and you get a list — a single expression with multiple parts:

user=> '123
123
user=> '"foo"
"foo"
user=> '(1 2 3)
(1 2 3)

Lists can hold anything, including other lists, which allows for nesting:

user=> '(nil "hi")
(nil "hi")
user=> '(1 (2 (3 ())))
(1 (2 (3 ())))

This nested structure resembles a tree, which is fitting: human languages work the same way. Sentences contain clauses, which nest inside one another, with modifiers attached to their subjects and verbs. A Lisp list is the same idea made concrete.

Consider the simple thought “increment the number zero.” As a tree:

Increment
  the number zero

You have a symbol for incrementing and a number zero. Combine them inside a list:

clj=> '(inc 0)
(inc 0)

Because it’s quoted, that list remains just the text of the expression — no interpretation happens. Remove the quote, and Clojure takes the expression seriously:

user=> (inc 0)
1

Incrementing zero yields one. Want to increment that result too? Nested lists handle it:

Increment
  increment
    the number zero
user=> (inc (inc 0))
2

Every Lisp sentence is a list that starts with a verb and is followed by zero or more arguments. A nested list is evaluated before the outer one, just as a subordinate clause resolves within its larger sentence.

Trace what happens as Clojure processes this expression:

(inc (inc 0))

First, it resolves the symbols in the code:

(#<core$inc clojure.core$inc@6f7ef41c>
  (#<core$inc clojure.core$inc@6f7ef41c>
    0))

Then the innermost list (inc 0) reduces to the number 1:

(#<core$inc clojure.core$inc@6f7ef41c>
 1)

Finally, the outer list increments that 1:

2

The rule is simple and universal: lists start with a verb, parts evaluate left to right, and innermost lists evaluate first.

(+ 1 (- 5 2) (+ 3 4))
(+ 1 3       (+ 3 4))
(+ 1 3       7)
11

That’s the whole grammar of Lisp — the structure behind every expression in the language. Evaluation works by substituting meanings for symbols until a value remains. This is the essence of the Lambda Calculus, the theoretical model underlying nearly all programming languages. Ruby, JavaScript, C and Haskell each express programs differently on the surface, but every one of them constructs an internal tree of expressions. Lisp just makes that tree explicit in its syntax.

Wrapping Up

With the basic nouns — numbers like 5, strings like "cat", and symbols like inc — plus quoting to distinguish an expression from its evaluated result, the mechanics of lists and nesting form a complete foundation for expressing computation. From here, the next step is expanding the vocabulary of verbs and nouns to model more complex values and transformations.