Skip to content
AgileLogics
6 min read

Reading Clojure Code

Prerequisites: Your first program

Clojure's syntax fits on one page. Once you can read it, everything else is just learning functions.

Prefix notation

In most languages you write 1 + 2 * 3. In Clojure, the operator comes first, inside parentheses:

(println (+ 1 (* 2 3)))
(println (+ 1 2 3 4 5))
(println (< 1 2 3))
(println (max 4 9 2))
7
15
true
9
  • A form (f a b c) calls f with the arguments a, b, c.
  • Operators like + and < are ordinary functions, so they take any number of arguments: (< 1 2 3) checks that the numbers increase.
  • There's no operator precedence to remember — the nesting says it all.

The building blocks

(println 42 3.14 "text" \c true nil)   ; numbers, string, character, boolean, nil
(println :keyword 'symbol)             ; a keyword and a quoted symbol
(println [1 2 3])                      ; vector
(println '(1 2 3))                     ; list (quoted, so it isn't called)
(println {:name "Ada" :age 36})        ; map
(println #{:red :green})               ; set
42 3.14 text c true nil
:keyword symbol
[1 2 3]
(1 2 3)
{:name Ada, :age 36}
#{:green :red}

Code is made of the same data structures: a program is lists of symbols, numbers, strings, vectors, and maps. That's what "code is data" (homoiconicity) means. Commas are treated as whitespace, so {:a 1, :b 2} and {:a 1 :b 2} are the same.

Evaluation rules

  1. Numbers, strings, keywords, true, false, and nil evaluate to themselves.
  2. A symbol such as println or x evaluates to the value it names.
  3. A list is a call: evaluate the first item (a function), evaluate the arguments, then call.
  4. A few special forms (def, if, let, fn, do, quote, ...) and macros have their own rules — for example, if evaluates only one branch.
  5. 'x (quote) stops evaluation: '(1 2 3) is a list of data, not a call to 1.

When things go wrong

Clojure reads and evaluates a file one top-level form at a time, so forms before an error still run:

;; illustrative — shows an error
(println "start")
(println (/ 10 0))
(println "never reached")
start
Execution error (ArithmeticException) at user/eval3 (main.clj:3).
Divide by zero
;; illustrative — shows an error
(println "start")
(println (undefined-fn 1))
start
Syntax error compiling at (main.clj:3:10).
Unable to resolve symbol: undefined-fn in this context

The message names the kind of error, the file, and the line (and column). Clojure then prints Full report at: and the path of a file with the complete stack trace — trimmed here. A missing closing parenthesis gives "EOF while reading", pointing at the line where the unfinished form started.