2016-12-18 3 views
6

Clojureツールボックスで最も慣用的なツールの1つであるswap!の機能は、instance?のチェックを行います。多態性(プロトコル)を優先するために、タイプチェックの前後で条件文を実装しないようにプログラミングで言われています。 ClojureScriptは、原子に対して直接ISwapプロトコルを実装しておらず、その代わりに公開されているswap! apiが、その対象がアトムであるかどうかを確認した後でのみプロトコルに戻ることは奇妙に思えます。なぜClojureScriptの原子は完全なプロトコルを実装していないのですか?

原子は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)))) 

答えて

2

それは、パフォーマンス関連のあるように見えます:http://dev.clojure.org/jira/browse/CLJS-760

-resetとIAtomプロトコルを追加!メソッドとcljs.core/reset!のAtomの高速パスです。

はjsperfここを参照してください - http://jsperf.com/iatom-adv

最新のChromeとFirefoxのバージョンは〜20〜30%の減速を被ります。古いバージョンのfirefoxは60-70%まで苦しんでいます。

チケットをさらにダウンして、IAtomをIResetとISwapの2つのプロトコルに分割することにしました。しかし、これはデイビッドが行った実装であり、型チェックが行われています。スピードを回復するために行われたと思います。

残念ながら、アトムがなぜ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 <[email protected]> 
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 

それはチケットがデュアル性格であることを助けていません。 "、または"リセットを使用する他のものが十分に速く実行されていない ")と設計上の問題("私たちはIAtomプロトコルが必要です ")。問題は、他の実装が検証に苦しんで、実際に原子ではないにもかかわらずウォッチャーのコストを通知しなければならないということでした。私はそれがはっきりしたことを望む。

私がClojure/Scriptのコミットについて気に入っていないことの1つは、それほど説明的ではないということです。適切な背景情報を持つカーネルにもっと似ていて、物事がどのようになったのかを把握しようとする人々(私たちのような人)がもっと役に立つ情報を得ることができるよう、

関連する問題