Pointers in Clojure

In Clojure, there isn’t a direct equivalent to pointers as in lower-level languages. However, we can demonstrate similar concepts using atoms, which provide a way to manage shared, synchronous, independent state.

(ns pointers-example
  (:require [clojure.core :as c]))

;; We'll show how atoms work in contrast to values with
;; 2 functions: `zeroval` and `zeroatom`. `zeroval` has an
;; integer parameter, so arguments will be passed to it by
;; value. `zeroval` will get a copy of `ival` distinct
;; from the one in the calling function.

(defn zeroval [ival]
  (def ival 0))

;; `zeroatom` in contrast takes an atom. The `reset!` function
;; in the function body then changes the value of the atom.
;; This change will be visible outside the function.

(defn zeroatom [iatom]
  (reset! iatom 0))

(defn -main []
  (let [i (atom 1)]
    (println "initial:" @i)

    (zeroval @i)
    (println "zeroval:" @i)

    (zeroatom i)
    (println "zeroatom:" @i)

    ;; In Clojure, we don't print memory addresses,
    ;; but we can print the atom itself
    (println "atom:" i)))

;; To run the program:
;; $ clj -m pointers-example

In this Clojure version:

  1. We use atom to create a mutable reference to a value.
  2. The zeroval function doesn’t change the original value because it receives a dereferenced value.
  3. The zeroatom function changes the value of the atom using reset!.
  4. We use @ to dereference the atom and get its current value.

When you run this program, you’ll see:

initial: 1
zeroval: 1
zeroatom: 0
atom: #atom[0 0x12345678]

zeroval doesn’t change the i in main, but zeroatom does because it has a reference to the atom itself.

In Clojure, atoms provide a way to manage shared state that’s somewhat analogous to using pointers for mutable state in languages like C or Go. However, atoms are designed to be safe for concurrent access and mutation, which is a key feature of Clojure’s approach to managing state.