【问题标题】:Why do ClojureScript atoms not implement full protocol?为什么 ClojureScript 原子没有实现完整的协议?
【发布时间】:2016-12-18 22:53:37
【问题描述】:

swap! 函数是 Clojure 工具箱中最常用的工具之一,它执行instance? 检查。我们在编程中被告知要避免围绕类型检查实现条件,而更喜欢多态性(协议)。 ClojureScript 没有直接针对原子实现 ISwap 协议,而是在公共 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))))

【问题讨论】:

    标签: clojure clojurescript


    【解决方案1】:

    看起来和性能有关: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。但这是 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
    

    票证具有双重性质并没有帮助:性能是一个问题(虽然不清楚是什么方式:“原子运行速度不够快”,或“其他使用重置的东西!运行速度不够快” ?) 和一个设计问题(“我们想要一个 IAtom 协议”)。我认为问题在于,即使它们不是真正的原子,其他实现也必须承受验证和通知观察者的成本。我希望它更清楚。

    我不喜欢 Clojure/Script 中的提交的一件事是它们的描述性不是很强。我希望它们更像内核,并带有适当的背景信息,以便那些试图弄清楚事情如何发展的人(比如我们)可以获得更多有用的信息。

    【讨论】:

      猜你喜欢
      • 2015-07-14
      • 2016-09-29
      • 1970-01-01
      • 2013-07-01
      • 2016-09-17
      • 2015-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多