Clojure Syntax Basics
Clojure syntax is built entirely from s-expressions: nested lists surrounded by parentheses where the first element is evaluated as a function or macro and the remaining elements are its arguments. Unlike most mainstream languages, Clojure uses consistent prefix notation everywhere — (+ 1 2) rather than 1 + 2 — and every form, not just expressions in the traditional sense, evaluates to a value.
Cricket analogy: In Clojure, every form is an expression that returns a value, much like every ball bowled in a T20 match produces a definite outcome recorded on the scoreboard — there's no action that just happens and vanishes, because (+ 1 2) always evaluates to 3, not merely 'runs 3.'
Literals and Data Types
Clojure has literal syntax for numbers (integers, doubles, and ratios like 1/3), strings in double quotes, booleans true and false, and nil to represent an intentional absence of value. It also has two special reference types: symbols, which name and refer to bound values, and keywords like :active, which are self-evaluating constants commonly used as map keys or enumerated tags.
Cricket analogy: Clojure's keyword :not-out is like a fixed scoring code on a scorecard — unlike a batter's name, which refers to something else, a keyword like :not-out always evaluates to itself, the same way 'NO' on a scorecard is a fixed abbreviation, not a reference to another player.
Collections: Lists, Vectors, Maps, and Sets
Clojure provides four core collection literals: lists '(1 2 3), which are optimized for adding to the front and sequential traversal; vectors [1 2 3], which support fast indexed access and appending to the end; maps {:k1 v1 :k2 v2}, which associate keys with values; and sets #{1 2 3}, which store unique, unordered elements. Choosing the right collection type for the access pattern you need is a core Clojure skill.
Cricket analogy: A Clojure vector like [4 6 0 1] recording ball-by-ball outcomes preserves order and allows fast indexed access to, say, the 3rd ball of the over, the way a scorer instantly looks up 'what happened on ball 3' without scanning the whole over.
Defining Values and Functions
The def special form binds a name to a value at the top level, e.g. (def pi 3.14159). The defn macro combines def with fn to define a named function in one step: (defn square [x] (* x x)). For short, throwaway logic, Clojure also offers anonymous function syntax via fn or the compact #(...) reader macro, where % refers to the single argument.
Cricket analogy: Defining a function with defn is like the ICC formally codifying a new law in the Laws of Cricket, such as the 2017 update on bat-edge width limits — once (defn calculate-strike-rate [runs balls] (* 100 (/ runs balls))) is defined, that named operation can be reused identically in every future 'match' of your program.
;; Literals and basic values
(def pi 3.14159)
(def greeting "Hello, Clojure!")
(def status :active)
;; A vector, a map, and a set
(def scores [98 87 92 100])
(def player {:name "Ada" :level 12 :class :wizard})
(def unique-tags #{:beginner :clojure :functional})
;; Defining a named function
(defn celsius->fahrenheit [c]
(+ 32 (* c (/ 9 5))))
;; An anonymous function used inline
(map #(* % %) [1 2 3 4])
;; => (1 4 9 16)
(println (celsius->fahrenheit 100))
;; => 212In Clojure, nil and the boolean false are the only 'falsy' values in conditional contexts — every other value, including 0, empty strings, and empty collections, is treated as truthy. This trips up many newcomers coming from languages like Python or JavaScript where 0 and empty collections are falsy.
- Every Clojure form is an expression that evaluates to a value; there are no bare statements.
- Clojure uses prefix notation: the operator or function always comes first inside parentheses, e.g. (+ 1 2).
- Core literal types include numbers, strings, keywords (:like-this), symbols, booleans, and nil.
- Lists '(1 2 3) are optimized for sequential access; vectors [1 2 3] support fast indexed lookup.
- Maps {:k v} associate keys with values; sets #{1 2 3} store unique, unordered elements.
defbinds a name to a value;defndefines a named function.- Anonymous functions can be written with
fnor the shorthand#(...)syntax for quick, inline use.
Practice what you learned
1. What does Clojure's prefix notation mean in practice?
2. Which of the following is true about Clojure keywords, such as :active?
3. Which Clojure collection type guarantees unique elements with no duplicates?
4. What is the difference between `def` and `defn`?
5. In Clojure, which values are considered 'falsy' in a conditional?
Was this page helpful?
You May Also Like
What Is Clojure?
An introduction to Clojure — a modern, functional Lisp dialect that runs on the JVM and emphasizes immutability and simplicity.
The REPL and Forms
How Clojure's Read-Eval-Print Loop works, how forms are evaluated, and why REPL-driven development is central to the Clojure workflow.
Your First Clojure Program
Write, structure, and run a complete first Clojure program, covering namespaces, function composition, and program entry points.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics