Атомы
Атомы позволяют управлять общими, синхронными, независимыми состояниями. Они являются ссылочными типами как ссылки и переменные. Вы создаете атом с помощью atom и можете осуществлять доступ к его состоянию с помощью deref/@. Как ссылки и агенты, атомы поддерживают валидаторы. Чтобы изменить значение атома, вы можете использовать swap!. Также предоставляется низкоуровневая функция compare-and-set!. Изменения в атомах всегда защищены от состояния гонки.
Как и у всех ссылочных типов, предполагаемое использование атомов - это хранение одной из неизменяемых структур Clojure. Также вы можете изменять атом применяя к старому значению некоторую функцию. Это можно сделать с помощью swap!. Внутри себя swap! читает текущее значение, применяет функцию к нему и пытается выполнить compare-and-set!. Так как другой поток может изменить значение в тоже самое время, возможно придется повторить попытку, а возможно и не один раз. В результате значение всегда будет равно результату применения предоставленной функции к текущему значению, в каждый момент времени. Заметим, что так как функция может быть вызвана несколько раз, она не должна иметь побочных эффектов.
Атомы являются эффективным средством представления некоторого состояния, которое не координируется ни с каким другим состоянием и для которого вы хотите реализовать синхронные изменения (в отличие от агентов, которые также независимы, но асинхронны). Типичный пример использования атомов - мемоизация:
(defn memoize [f]
(let [mem (atom {})]
(fn [& args]
(if-let [e (find @mem args)]
(val e)
(let [ret (apply f args)]
(swap! mem assoc args ret)
ret)))))
(defn fib [n]
(if (<= n 1)
n
(+ (fib (dec n)) (fib (- n 2)))))
(time (fib 35))
user=> "Elapsed time: 941.445 msecs"
(def fib (memoize fib))
(time (fib 35))
user=> "Elapsed time: 0.044 msecs"