Skip to content
AgileLogics
5 min read

def and Immutable Values

Prerequisites: Reading Clojure code

def gives a value a name at the top level of a namespace (a module; the default one in scripts is user):

(def city "Lagos")
(def temperature 24)

(println "It is" temperature "degrees in" city)
(println (+ temperature 3))
(println temperature)
It is 24 degrees in Lagos
27
24

(+ temperature 3) didn't change temperature — it returned a new value. In Clojure, values never change. Numbers, strings, and also vectors, maps, and sets are immutable.

Immutable collections

(def fruits ["apple" "banana"])
(def more-fruits (conj fruits "cherry"))

(println fruits)
(println more-fruits)
[apple banana]
[apple banana cherry]

conj ("conjoin") returned a new vector with an extra item; fruits is untouched. This sounds wasteful, but Clojure's persistent data structures share almost all their memory between versions, so a "copy" costs very little.

Why it's worth it:

  • Any function can receive your data without being able to break it.
  • Values can be shared between threads with no locks.
  • Old versions remain available, which makes undo, history, and debugging easy.

Redefining

(def greeting "Hello")
(def greeting "Hi")
(println greeting)
Hi

Evaluating def again replaces what the name refers to. That's meant for the REPL workflow (re-evaluating a definition while you develop), not for program logic — never use def like an assignment inside a function. For local names, use let (a few lessons ahead); for changing state, use an atom (Level 2).

Naming

  • Names are kebab-case: max-retries, user-name.
  • Almost any character is allowed: valid? (returns a boolean), swap! (changes state), ->json (converts), *debug* (a dynamic var).
  • Names are case-sensitive.

Practice

Define product and price, then print Coffee costs 3.5.

Show solution
(def product "Coffee")
(def price 3.5)
(println product "costs" price)
Coffee costs 3.5