Clojure has an immutable data model. However, this doesn't mean that we can't represent data and change it over time. Direct change or mutation should be avoided at all costs. If necessary, a binding
macro will create new var
values (or bindings) that are local only to a binding
context or scope. These Vars must be marked as ^:dynamic
and they should already exist:
(def ^:dynamic a 1) (def ^:dynamic b 2) (binding [a 10 b 20] (+ a b)) ; => 30 a ; => 1 b ; => 2
It is, however, preferable to choose one of four reference types: Var, Atom, Agent, and Ref. Doing this requires an understanding of Clojure's notion of identity, value, and time. In his presentation Are We There Yet? (you can read more at http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey), Clojure creator, Rich Hickey, describes most computer languages as having variables that allow programmers to set and change a value in the program's runtime. This model assumes, however, that the world stops while you look at or change it (you can read more at http://clojure.org/state). Clojure's approach includes having an immutable value, such as Sally's address, and forever maintaining this value at a point in time. Sally's address, however, is just a value in 2014. Clojure sees identity as a continuous logical entity that has a series of different values over time. So, Sally's home, would be an identity where the value of her address in 2014 may be different from her address in 2015. As we've seen, Clojure's core functions take immutable values and return new values that are updates of the original. The two values share data structures under the hood (Clojure's Persistent Data Structures), which is possible since each value won't change. This succession of data updates is how we disambiguate between values over time. These values often need to be shared with other threads of execution. The Var, Atom, Agent, and Ref reference types are ways of managing shared identities that have a particular state at a given point in time. These reference types can change using a prescribed set of functions to create, update, and reset their state:
ref
, update it using alter
or commute
, and reset it using ref-set
.(def one (ref [])) ;; #object[clojure.lang.Ref 0x1fbb15f {:status :ready, :val []}] (dosync (alter one conj 12)) ;; [12] (deref one) ;; [12] @one ;; [12] (dosync (ref-set one [:fubar])) ;; [:fubar] @one ;; [:fubar]
atom
, update it using swap!
, and reset it using reset!
:(def two (atom [])) ;; #object[clojure.lang.Atom 0xe2fb45 {:status :ready, :val []}] (swap! two conj 12) ;; [12] @two ;; [12] (reset! two [:fubar]) ;; [:fubar] @two ;; [:fubar]
agent
, update it using send
or send-off
, and reset it using restart-agent
.(send three conj 12) ;; #object[clojure.lang.Agent 0x1198a8c {:status :ready, :val []}] @three ;; [12] (restart-agent three [:fubar]) java.lang.RuntimeException Agent does not need a restart Util.java: 221 clojure.lang.Util/runtimeException Agent.java: 210 clojure.lang.Agent/restart core.clj: 2078 clojure.core/restart-agent RestFn.java: 425 clojure.lang.RestFn/invoke six.clj: 66 edgaru.six.six/eval14571 (send three inc) java.lang.ClassCastException clojure.lang.PersistentVector cannot be cast to java.lang.Number (restart-agent three [:fubar]) ;; [:fubar] @three ;; [:fubar]
def
, update them using alter-var-root
, and reset them using var-set
:(def four []) ;; [] (alter-var-root (var four) conj 12) ;; [12] four ;; [12]
3.143.235.23