两者兼而有之。存储桶是一个值,一个函数将获取该值并生成一个基于原始值(和共享未更改值)的新值:
user> (def bucket {:volume 100 :rate 10 :poured 0})
#'user/bucket
user> (assoc bucket
:volume (- (:volume bucket) (:rate bucket))
:poured (+ (:poured bucket) (:rate bucket)))
{:rate 10, :volume 90, :poured 10}
它将原始存储桶留在名为bucket的变量中
user> bucket
{:rate 10, :volume 100, :poured 0}
所以我们可以在桶值上定义一个函数:
user> (defn pour [bucket]
(assoc bucket
:volume (- (:volume bucket) (:rate bucket))
:poured (+ (:poured bucket) (:rate bucket))))
#'user/pour
user> (pour bucket)
{:rate 10, :volume 90, :poured 10}
user> bucket
{:rate 10, :volume 100, :poured 0}
当我们想要表达桶“身份”的概念时,这将非常有用。 identity carries values from state to state 非常有用。我将使用原子,因为我希望一次同步更新单个身份/事物。
user> (def bucket (atom {:volume 100 :rate 10 :poured 0}))
#'user/bucket
user> (swap! bucket pour)
{:rate 10, :volume 90, :poured 10}
user> (swap! bucket pour)
{:rate 10, :volume 80, :poured 20}
user> (swap! bucket pour)
{:rate 10, :volume 70, :poured 30}
现在我们的存储桶随着时间的推移而变化,这些变化对每个人都是可行的。值得注意的是,产生新值的函数完全独立于它被用来改变原子并且可以在其他地方重用的事实。一些 Clojurians 将此称为“simple”
您通常会想要计算诸如“将桶倒三次的结果是什么,这可以通过将每个对 pour 的调用嵌套在下一个中很好地表达,如下所示:
user> (pour (pour (pour bucket)))
{:rate 10, :volume 70, :poured 30}
这种模式很常见,有一个两个字符的宏来表达它:->
user> (-> bucket pour pour pour)
{:rate 10, :volume 70, :poured 30}
在实践中,我通过嵌套和线程更多看到 clojure compose 函数,而不是通过可变状态系统(原子、引用、代理、变量)