Why is Clojure allowing me to redefine the same variable?

Viewed 116

If the variables are immutable in Clojure, then why did it allow to redefine the same variable in the following snippet:

(ns tutorial.test-immutability-of-variables)

(defn test_if_variables_are_mutable
  []
  (def a 1)
  (def a 10)
  (println (format "Value of a: %d" a) )
  )

(test_if_variables_are_mutable)

The above code compiles and o/p is 10.

2 Answers

Clojure makes a distinction between values and references to values. Clojure guarantees that values are immutable.

Consider what happens when your function is evaluated:

(def a 1)

When the above form is evaluated, a Clojure Var is created (with name a and namespace tutorial.test-immutability-of-variables) and it is bound to the value 1.

(def a 10)

When this form is evaluated, the existing Clojure Var is rebound to the value 10.

Use of def within a function is strongly discouraged. Use def for top level forms only in your code. Alternatively for a lexically bound symbol use let.

The full story of what happens is a bit more involved than I've outlined above, see the reference documentation on Vars. There are other reference types in Clojure, such as Atoms.

As the other answer points out, you can change what the global Var points to, but you cannot change the original value. Here is another example:


(def x 42)

(defn changer-1
  []
  (println "changer-1: x - old" x)
  (let [x "Goodbye"] ; change a local variable
    (println "changer-1: x - new" x)))

(defn caller-1
  []
  (newline)
  (let [x "Hello"]
    (println "caller-1: x - before" x)
    (changer-1)
    (println "caller-1: x - after " x)))

(defn changer-2
  []
  (println "changer-2: x - old" x)
  (def x 666) ; reset the global Var (normally bad form)
  (println "changer-2: x - new" x))

(defn caller-2
  []
  (newline)
  (println "caller-2: Var x - before" x)
  (let [x "Hello"]
    (println "caller-2: x - before" x)
    (changer-2)
    (println "caller-2: x - after " x))
  (println "caller-2: Var x - after" x))


(caller-1)
(caller-2)

with result

-----------------------------------
   Clojure 1.10.3    Java 15.0.2
-----------------------------------

caller-1: x - before Hello
changer-1: x - old 42
changer-1: x - new Goodbye
caller-1: x - after  Hello

caller-2: Var x - before 42
caller-2: x - before Hello
changer-2: x - old 42
changer-2: x - new 666
caller-2: x - after  Hello
caller-2: Var x - after 666

For more information, please see this list of documentation sources, especially "Getting Clojure" and "Brave Clojure"

Related