为什么 ClojureScript 原子不实现完整的协议?
Why do ClojureScript atoms not implement full protocol?
swap!
函数是 Clojure 工具箱中最惯用的工具之一,可以进行 instance?
检查。我们在编程中被告知要避免围绕类型检查实施条件,而更喜欢多态性(协议)。奇怪的是,ClojureScript 没有直接针对原子实现 ISwap
协议,而是在 public swap!
api 中仅在检查主题是否为一个原子。
我认为这种策略一定是出于性能原因而使用的,因为原子是 swap!
和许多其他原子方法的主要用例。这样对吗?
我更愿意将原子的 api 实现为实际协议的一部分,这样就没有必要了。
(defn swap!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects. Returns
the value that was swapped in."
([a f]
(if (instance? Atom a)
(reset! a (f (.-state a)))
(-swap! a f)))
([a f x]
(if (instance? Atom a)
(reset! a (f (.-state a) x))
(-swap! a f x)))
([a f x y]
(if (instance? Atom a)
(reset! a (f (.-state a) x y))
(-swap! a f x y)))
([a f x y & more]
(if (instance? Atom a)
(reset! a (apply f (.-state a) x y more))
(-swap! a f x y more))))
看起来是性能相关的:http://dev.clojure.org/jira/browse/CLJS-760
Add an IAtom protocol with a -reset! method and a fast path for Atom in cljs.core/reset!.
See jsperf here - http://jsperf.com/iatom-adv
Latest chrome and firefox versions suffer ~20-30% slowdown. Older firefox versions suffer up to 60-70%.
进一步深入,决定将 IAtom 拆分为两个协议:IReset 和 ISwap。但这是 David 使用的实现,它进行类型检查,我想这样做是为了加快速度。
不幸的是,不清楚为什么 Atom 没有为此实现 IReset(和 ISwap),也不清楚为什么没有寻找这些东西。而且也不清楚原始补丁是如何工作的。它基本上采用了 reset!
的实现并将其置于 instance
检查之下,并为其添加了 -reset!
路径:
diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs
index 9fed929..c6e41ab 100644
--- a/src/cljs/cljs/core.cljs
+++ b/src/cljs/cljs/core.cljs
@@ -7039,6 +7039,9 @@ reduces them without incurring seq initialization"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Reference Types ;;;;;;;;;;;;;;;;
+(defprotocol IAtom
+ (-reset! [o new-value]))
+
(deftype Atom [state meta validator watches]
IEquiv
(-equiv [o other] (identical? o other))
@@ -7088,14 +7091,16 @@ reduces them without incurring seq initialization"
"Sets the value of atom to newval without regard for the
current value. Returns newval."
[a new-value]
+ (if (instance? Atom a)
(let [validate (.-validator a)]
(when-not (nil? validate)
- (assert (validate new-value) "Validator rejected reference state")))
+ (assert (validate new-value) "Validator rejected reference state"))
(let [old-value (.-state a)]
(set! (.-state a) new-value)
(when-not (nil? (.-watches a))
- (-notify-watches a old-value new-value)))
- new-value)
+ (-notify-watches a old-value new-value))
+ new-value))
+ (-reset! a new-value)))
(defn swap!
"Atomically swaps the value of atom to be:
这是在 33692b79a114faf4bedc6d9ab38d25ce6ea4b295 中提交的(或者至少是非常接近它的日期)。然后其他协议更改在 3e6564a72dc5e175fc65c9767364d05af34e4968:
commit 3e6564a72dc5e175fc65c9767364d05af34e4968
Author: David Nolen <david.nolen@gmail.com>
Date: Mon Feb 17 14:46:10 2014 -0500
CLJS-760: break apart IAtom protocol into IReset & ISwap
diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs
index 25858084..e4df4420 100644
--- a/src/cljs/cljs/core.cljs
+++ b/src/cljs/cljs/core.cljs
@@ -7061,9 +7061,12 @@ reduces them without incurring seq initialization"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Reference Types ;;;;;;;;;;;;;;;;
-(defprotocol IAtom
+(defprotocol IReset
(-reset! [o new-value]))
+(defprotocol ISwap
+ (-swap! [o f] [o f a] [o f a b] [o f a b xs]))
+
(deftype Atom [state meta validator watches]
IEquiv
(-equiv [o other] (identical? o other))
@@ -7124,21 +7127,33 @@ reduces them without incurring seq initialization"
new-value))
(-reset! a new-value)))
+;; generic to all refs
+;; (but currently hard-coded to atom!)
+(defn deref
+ [o]
+ (-deref o))
+
(defn swap!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects. Returns
the value that was swapped in."
([a f]
- (reset! a (f (.-state a))))
+ (if (instance? Atom a)
+ (reset! a (f (.-state a)))
+ (-swap! a (deref a))))
([a f x]
- (reset! a (f (.-state a) x)))
+ (if (instance? Atom a)
+ (reset! a (f (.-state a) x))
+ (-swap! a (f (deref a) x))))
([a f x y]
- (reset! a (f (.-state a) x y)))
- ([a f x y z]
- (reset! a (f (.-state a) x y z)))
- ([a f x y z & more]
- (reset! a (apply f (.-state a) x y z more))))
+ (if (instance? Atom a)
+ (reset! a (f (.-state a) x y))
+ (-swap! a (f (deref a) x y))))
+ ([a f x y & more]
+ (if (instance? Atom a)
+ (reset! a (apply f (.-state a) x y more))
+ (-swap! a (f (deref a) x y more)))))
(defn compare-and-set!
"Atomically sets the value of atom to newval if and only if the
@@ -7149,13 +7164,6 @@ reduces them without incurring seq initialization"
(do (reset! a newval) true)
false))
-;; generic to all refs
-;; (but currently hard-coded to atom!)
-
-(defn deref
- [o]
- (-deref o))
-
(defn set-validator!
"Sets the validator-fn for an atom. validator-fn must be nil or a
side-effect-free fn of one argument, which will be passed the intended
票证是双重性质的,这无济于事:性能是一个问题(虽然不清楚是什么原因:"atoms are not operating fast enough",还是"other things using reset! are not running fast enough"?)和设计问题("we want an IAtom protocol").我认为问题是其他实现将不得不承受验证和通知观察者的成本,即使它们不是真正的原子。我希望它更清楚。
关于 Clojure/Script 中的提交,我不喜欢的一件事是它们描述性不强。我希望它们更像内核,并提供适当的背景信息,以便那些试图弄清楚事情是如何发生的人(比如我们)获得更多有用的信息。
swap!
函数是 Clojure 工具箱中最惯用的工具之一,可以进行 instance?
检查。我们在编程中被告知要避免围绕类型检查实施条件,而更喜欢多态性(协议)。奇怪的是,ClojureScript 没有直接针对原子实现 ISwap
协议,而是在 public swap!
api 中仅在检查主题是否为一个原子。
我认为这种策略一定是出于性能原因而使用的,因为原子是 swap!
和许多其他原子方法的主要用例。这样对吗?
我更愿意将原子的 api 实现为实际协议的一部分,这样就没有必要了。
(defn swap!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects. Returns
the value that was swapped in."
([a f]
(if (instance? Atom a)
(reset! a (f (.-state a)))
(-swap! a f)))
([a f x]
(if (instance? Atom a)
(reset! a (f (.-state a) x))
(-swap! a f x)))
([a f x y]
(if (instance? Atom a)
(reset! a (f (.-state a) x y))
(-swap! a f x y)))
([a f x y & more]
(if (instance? Atom a)
(reset! a (apply f (.-state a) x y more))
(-swap! a f x y more))))
看起来是性能相关的:http://dev.clojure.org/jira/browse/CLJS-760
Add an IAtom protocol with a -reset! method and a fast path for Atom in cljs.core/reset!.
See jsperf here - http://jsperf.com/iatom-adv
Latest chrome and firefox versions suffer ~20-30% slowdown. Older firefox versions suffer up to 60-70%.
进一步深入,决定将 IAtom 拆分为两个协议:IReset 和 ISwap。但这是 David 使用的实现,它进行类型检查,我想这样做是为了加快速度。
不幸的是,不清楚为什么 Atom 没有为此实现 IReset(和 ISwap),也不清楚为什么没有寻找这些东西。而且也不清楚原始补丁是如何工作的。它基本上采用了 reset!
的实现并将其置于 instance
检查之下,并为其添加了 -reset!
路径:
diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs
index 9fed929..c6e41ab 100644
--- a/src/cljs/cljs/core.cljs
+++ b/src/cljs/cljs/core.cljs
@@ -7039,6 +7039,9 @@ reduces them without incurring seq initialization"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Reference Types ;;;;;;;;;;;;;;;;
+(defprotocol IAtom
+ (-reset! [o new-value]))
+
(deftype Atom [state meta validator watches]
IEquiv
(-equiv [o other] (identical? o other))
@@ -7088,14 +7091,16 @@ reduces them without incurring seq initialization"
"Sets the value of atom to newval without regard for the
current value. Returns newval."
[a new-value]
+ (if (instance? Atom a)
(let [validate (.-validator a)]
(when-not (nil? validate)
- (assert (validate new-value) "Validator rejected reference state")))
+ (assert (validate new-value) "Validator rejected reference state"))
(let [old-value (.-state a)]
(set! (.-state a) new-value)
(when-not (nil? (.-watches a))
- (-notify-watches a old-value new-value)))
- new-value)
+ (-notify-watches a old-value new-value))
+ new-value))
+ (-reset! a new-value)))
(defn swap!
"Atomically swaps the value of atom to be:
这是在 33692b79a114faf4bedc6d9ab38d25ce6ea4b295 中提交的(或者至少是非常接近它的日期)。然后其他协议更改在 3e6564a72dc5e175fc65c9767364d05af34e4968:
commit 3e6564a72dc5e175fc65c9767364d05af34e4968
Author: David Nolen <david.nolen@gmail.com>
Date: Mon Feb 17 14:46:10 2014 -0500
CLJS-760: break apart IAtom protocol into IReset & ISwap
diff --git a/src/cljs/cljs/core.cljs b/src/cljs/cljs/core.cljs
index 25858084..e4df4420 100644
--- a/src/cljs/cljs/core.cljs
+++ b/src/cljs/cljs/core.cljs
@@ -7061,9 +7061,12 @@ reduces them without incurring seq initialization"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Reference Types ;;;;;;;;;;;;;;;;
-(defprotocol IAtom
+(defprotocol IReset
(-reset! [o new-value]))
+(defprotocol ISwap
+ (-swap! [o f] [o f a] [o f a b] [o f a b xs]))
+
(deftype Atom [state meta validator watches]
IEquiv
(-equiv [o other] (identical? o other))
@@ -7124,21 +7127,33 @@ reduces them without incurring seq initialization"
new-value))
(-reset! a new-value)))
+;; generic to all refs
+;; (but currently hard-coded to atom!)
+(defn deref
+ [o]
+ (-deref o))
+
(defn swap!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects. Returns
the value that was swapped in."
([a f]
- (reset! a (f (.-state a))))
+ (if (instance? Atom a)
+ (reset! a (f (.-state a)))
+ (-swap! a (deref a))))
([a f x]
- (reset! a (f (.-state a) x)))
+ (if (instance? Atom a)
+ (reset! a (f (.-state a) x))
+ (-swap! a (f (deref a) x))))
([a f x y]
- (reset! a (f (.-state a) x y)))
- ([a f x y z]
- (reset! a (f (.-state a) x y z)))
- ([a f x y z & more]
- (reset! a (apply f (.-state a) x y z more))))
+ (if (instance? Atom a)
+ (reset! a (f (.-state a) x y))
+ (-swap! a (f (deref a) x y))))
+ ([a f x y & more]
+ (if (instance? Atom a)
+ (reset! a (apply f (.-state a) x y more))
+ (-swap! a (f (deref a) x y more)))))
(defn compare-and-set!
"Atomically sets the value of atom to newval if and only if the
@@ -7149,13 +7164,6 @@ reduces them without incurring seq initialization"
(do (reset! a newval) true)
false))
-;; generic to all refs
-;; (but currently hard-coded to atom!)
-
-(defn deref
- [o]
- (-deref o))
-
(defn set-validator!
"Sets the validator-fn for an atom. validator-fn must be nil or a
side-effect-free fn of one argument, which will be passed the intended
票证是双重性质的,这无济于事:性能是一个问题(虽然不清楚是什么原因:"atoms are not operating fast enough",还是"other things using reset! are not running fast enough"?)和设计问题("we want an IAtom protocol").我认为问题是其他实现将不得不承受验证和通知观察者的成本,即使它们不是真正的原子。我希望它更清楚。
关于 Clojure/Script 中的提交,我不喜欢的一件事是它们描述性不强。我希望它们更像内核,并提供适当的背景信息,以便那些试图弄清楚事情是如何发生的人(比如我们)获得更多有用的信息。