1

基本概念

Clojure 语言中没有简单的变量。但 Clojure 却声称最擅长管理状态,为什么?靠的是仔细设计的状态类型们,其中最引人注目和最常用的是 Atom 和 Ref(ClojureScript 中不可用)。

粗看起来,这两个东西区别很大:生成函数不同,操作函数也不同:

类型 生成函数 操作函数
Atom atom reset!, swap!
Ref ref ref-set, alter, commute

Ref 还要将操作包装在dosync中。

但在实际上,它们其实遵循同样的设计思路:所谓状态变量,指的是身份。例如:

(def person-data {:age 30, :salary 5000})
(def a-xiaoming (atom person-data))
(def r-xiaoming (ref person-data))

定义了两种不同的状态身份。它们实际上拥有同一个值:{:age 30},这个值是它的状态数据。在 clojure 语言中,状态身份是不会变的(可以理解为类似于 c 的指针),状态数据也不会变化,变化的是两边的联系。例如, 小明的个人数据是 person-data。这句话中,小明这个主体状态身份是不会变的,person-data 这个状态数据也不会变化,但小明年龄可能会关联到不同的状态数据,例如,过了年后可能关联为另一个状态数据 {:age 31, :weight 5500}

函数推动状态数据的关联变化

推动状态的身份和数据的联系的变化是发生了某动作,也就是函数。因此,驱动状态是这样发生的:



(defn new-year [person-data]
  (-> person-data
      (update :age inc)
      (update :salary #(* % 2))))
      
(swap! a-xiaoming #(update % :age inc :weight new-year)

注意,一个事件发生的函数应该是统一在一起的,它们是一个事务。clojure 的优秀数据结构保证这个事务的原子性。

下面这种写法呢?

(-> a-xiaoming
    (swap! #(update % :age inc))
    (swap! #(update :salary (fn [n] (* n 2)))))

事实上,对一个状态数据的连续操作一定是不正确的,它打破了事务的边界。ref 的dosync建立的边界也是一样。


robertluo
738 声望21 粉丝

« 上一篇
配置迷思

引用和评论

0 条评论