Clojure: Should I use atoms or refs?
After working with Clojure for a few days, recurring through loops to build up local variable values, I realized that I really needed to know how to handle state.
Of course, Clojure provides several ways to mutate the state of non-local variables. However, coming to Clojure from an object-oriented background, I was completely unfamiliar with the vocabulary of these reference types (atoms, refs, and agents) and, therefore, steered clear of them at first.
But, for my Clojure tic-tac-toe game, I really needed to know how to access user settings (e.g, difficulty level and player type), and this required setting and changing non-local variables.Â
I first explored atoms. Atoms are a reference type that allow you to assign an immutable Clojure data type to a non-local variable.
After defining the atom variable, you can modify its value any number of times within the namespace. You can also access the atom variable's value from anywhere in the namespace.
To create an atom, simply define a variable as usual, but call the atom function to assign the value to an atom. For example, my player-types variable might look like this:
(def player-types (atom []))
To access the value of player-types, you can now call @player-types:
Next, define a method to update the atom's value. To update the value of an atom, you can use either the swap! or the reset! functions. Both swap! and reset! set the atom's value to the new value and return the new value, but swap! modifies the existing value while reset! replaces it entirely.
For my player-types example, I created a  method called update-player-types to add player type values to the @player-types atom.
(defn update-player-types [new-type]
  (swap! player-types conj new-type))
Now, in my tic-tac-toe application, I can call this method with new values to add them to the @player-types atom.
(update-player-types "human")
(update-player-types "computer")
Now, within other methods, I can easily access these values:
=> (second @player-types)
Next, I looked into implementing the same variables using refs. Refs are variables that store values in a single location. They allow for the values to change through "transactions," which are triggered by using certain functions, including ref-set, alter, and commute.Â
To create a ref, define a variable and call the ref method on the value. For my player-types example, I call ref on an empty vector.
(def player-types (ref []))
Exactly like atoms, you can now access the ref value throughout the namespace by calling @player-types.
Similar to atoms, you'll now want to create a method for modifying the ref value. You can use ref-set, alter, or commute. Ref-set simply sets the new value regardless of the current value, alter changes the current value, and commute is similar to alter but it better handles concurrency issues resulting from order of operation dependencies.Â
Whichever ref function you choose, you must wrap it in a dosync function. The dosync function starts the transaction and runs the nested calls.
(defn update-player-type [new-type]
  (dosync (alter player-types conj new-type)))
After defining the update method, you can call it with the new values that you want to assign to the ref.
(update-player-type "human")
(update-player-type "computer")
=> (second @player-types)
As you can see, the implementation of the refs player-types example is almost exactly like the corresponding atom player-types. So, which one should I use in my tic-tac-toe application?
According to the documentation, refs should be used for synchronous, coordinated and shared changes, and atoms should be used for synchronous, independent and shared changes. In other words, refs are intended to be used when you need to update multiple refs within a single dosync transaction.
In the case of setting a tic-tac-toe game's player-types values, all changes to the values will be simple one-off updates. So, in this case, it makes more sense to use an atom rather than a ref.
Also, after implementing the player-types atom variable in my application, I realized that using a vector to hold the types wasn't the best approach because the atom vector still hangs on to all previously selected types.
Instead, I used two atom variables (x-player-type and o-player-type) and call reset! on them each time that I update the value.