Clojure
2026-08-20 09:15
Introducing Clojure taught you to read.
This unit gets you writing.
The longest, most reference-like unit in the module.
Almost everything here is a form to try at the REPL.
The REPL is not an accessory to learning Clojure.
It is where the learning happens.
By the end: the vocabulary to write small to medium programs.
Two lines. Why?
println is side-effecting — it prints, then returns nil.
Hello, world! printed during the evaluation phase.
nil printed during the print phase.
*1, *2, *3 — last, second-last, third-last successful form*e — the last errorEach success shifts the chain along.
This captures the value that was in *1.
*1 moves on. a-str does not.
The numbered variables stay put. Only *e changes.
doc — a function’s docstring and argument specfind-doc — search docstrings, when you do not know the nameapropos — search namesdoc output for +.
Function name first. Arguments after.
That is prefix notation.
And drops the comma — whitespace is enough.
Only the maths feels strange, because most languages special-case operators.
it makes it easy to generate and manipulate code
A nested list. An even number of expressions, in pairs.
Test, then result. Test, then result.
Generating that list: easy.
Generating a case statement in Java: not.
This is why the macro units later in this module are possible.
The printer uses them when echoing maps — purely for your eyes.
Everything other than
falseandnilis considered true.
That is the whole rule.
Both true. Note the leading periods — interop, from unit 1.
| Type | Examples | Contagiousness |
|---|---|---|
| Integer | 42, 0x2a, 2r101010 |
0 |
| Big integer | 42N |
1 |
| Ratio | 1/3, -2/4 |
2 |
| Big decimal | 2.78M |
3 |
| Floating point | 2.78 |
4 |
(/ 4 9) gives the ratio 4/9.
+' -' *' inc' dec' — a trailing quote means autopromote.
In (+ 1 2), the + is a symbol signifying the addition function.
Reading and evaluating are separate, so a symbol has two aspects:
its existence as data, and the value it resolves to.
valid — foo foo/bar ->Bar -foo foo? foo-bar foo+bar
invalid — /bar /foo +1foo
The quote says: this is literal data, not code.
A keyword is sort of like an autoquoted symbol.
Never references another value. Always evaluates to itself.
:foo :foo/bar :->foo :+
No namespace part → nil. name leaves strings unchanged.
Singly linked. Add and remove at the front.
The payoff: multiple lists can share tails.
Fails. The compiler reads lists as code — it tries to call 1.
Both the map and the keyword are callable.
New maps each time. Nothing was mutated.
assoc-in creates missing intermediate maps.
update-in takes a function, not a value.
first · rest · cons
Every collection supports them.
A map seen as a sequence is a sequence of pairs.
map, filter, remove, reduce never need to know
whether they were handed a list, a vector, or a map.
One abstraction carries the whole module.
A function is a value bound to a name. Exactly as unit 1 promised.
Not shorter. Legible. That is the point.
And bind to _ when you need the binding but not the value.
if takes single expressions for its branches:
Function bodies and when already have an implicit do.
Negated? · Else branch and implicit do?
if — has an alternative, takes single expressions
when — no alternative, but takes a body
So when saves you writing do.
The last value if all truthy — otherwise the actual falsey value that stopped it.
First truthy value, or the last falsey one.
This is what makes or usable for defaults.
= versus == for numeric equality.
while — polling something outside your controlloop/recur — stack-safe recursiondoseq, dotimes — side effectsmap, filter, remove, reduce — transformationfor — list comprehensionfactorial with loop/recur.
The recur is not last. Compile-time error.
Which is the good outcome — the compiler catches it.
Both return nil. That is the signal you called them for their effects.
Multiple sequences → multiple arguments. Result is as long as the shortest.
Same result. No helper function at all.
It builds a sequence. Multiple bindings nest.
Correct — and read in reverse of the order things happen.
Each result threads into the first argument of the next form.
as->, for when the value does not belong in first position.
They add no capability. They change legibility.
And they are macros — rewriting one expression shape into another before evaluation.
defn · when · cond · -> — you have used several already.
The sequence abstraction.
Three functions in one short section are why map, filter, remove, reduce and for work on anything you hand them.
doc make it explorable.false and nil is true; numbers form a tower with contagiousness.first, rest, cons unify every collection.recur, the sequence functions, for, then ->.Building Blocks of Clojure goes underneath all of this.
Higher-order functions · lexical closures · vars and binding
namespaces · destructuring · metadata and type hints
Clojure · Clojure Elements: Data Structures and Functions
Comments
One semicolon after code · two for a line · three for a block