TL;DR
- atom は ref より軽量な機構で、単一の値を他の値とは関係なく更新するのに使う。
- トランザクションを使わないので
dosyncは要らず、reset!で値を設定し、swap!で更新関数を適用する。swap!は再試行されうるので、渡す関数は副作用を起こしてはいけない。
内容
atom は ref よりも軽量な機構である。複数の ref の更新はトランザクションを使って協調させる必要があったが、atom は単一の値を、他の値とは関係なく更新するのに使う。
atom
atom は atom で作る。
(atom initial-state options?)
; 使用可能なオプション
; :validator validate-fn
; :meta metadata-mapref と同じように、音楽プレーヤーのサンプルを見てみる。
(def current-track (atom "Venus, the Bringer of Peace"))値を得るには、ref と同様に deref を使える。
(deref current-track) ; "Venus, the Bringer of Peace"reset!
atom はトランザクションを使わないので、dosync も必要ない。atom の値を設定するには reset! を使う。
(reset! an-atom newval)(reset! current-track "Credo")swap!
(swap! an-atom f & args)swap! は f を an-atom の現在の値に適用し、戻り値で an-atom を更新する。この関数と assoc を組み合わせると、atom で管理しているマップの一部のキーの値だけを変更できる。
(swap! current-track assoc :title "Sancte Deus")
; {:title "Sancte Deus", :composer "Byrd"}
@current-track
; {:title "Sancte Deus", :composer "Byrd"}他のスレッドが同じ atom を更新しようとした場合、swap! の呼び出し自身が再試行される可能性がある。そのため、swap! に渡す関数は副作用を起こしてはいけない。
ref も atom も同期的な更新で、更新関数が返ってきたら、その時点で値は既に更新されている。この制約が必要なく、実際の更新が起きるタイミングが少し遅れてもよいならば、agent が使える(Programming Clojure, Third Edition 第6章 agent を用いた非同期的な更新)。