Ok, trying a different way of asking this: Is it common practice in clojure to write data transforms in a way that will return the same object when possible (when the transform would be a noop), such as the mapv vs. reduce/assoc in my example? Would you do this to speed up equality checks, or to reduce gc pressure? Or is this an anti-pattern like using identical?
b.c. On Tuesday, February 25, 2014 9:59:11 AM UTC-8, David Nolen wrote: > > I don't really have anything to add to this thread but I will say that > Om's use of identical? is an implementation detail that's likely to change. > = already does an identical? check, ideally Om could use not= in the future > instead of (not (identical? ...)). > > David > > > On Tue, Feb 25, 2014 at 12:54 PM, Brian Craft <craft...@gmail.com<javascript:> > > wrote: > >> No, my question isn't "is there a way" to do this. I'm sure there are a >> dozen ways to do it. My question is about a specific way of doing it. In >> particular, if your solution does not involve calling (identical? ..), then >> it's not what I'm asking about. >> >> In om core there's a call to identical? under the :shouldComponentUpdate >> key, which I suspect is what David was talking about in his blog posts >> about avoiding deep compares via immutable data structures. My question is >> about whether that has implications for how you write algorithms that >> update state, and whether the semantics of update-in (or assoc, really) >> that allow it to return the same object if the update would return an >> identical object, are related to this mechanism, if these semantics are >> documented, and if they depend on the data type being assoc'd. >> >> >> >> On Monday, February 24, 2014 6:27:02 PM UTC-8, t x wrote: >> >>> Perhaps I mis-interpreted your question. >>> >>> I thought the question asked was: >>> >>> >>> GIven: >>> * pure function "func" >>> * some object "obj" >>> * (def a (func obj)) >>> * (def b (func obj)) >>> >>> Example: >>> * obj = [1 2 3] >>> * (defn func [lst] (map #(* 2 %) lst)) >>> >>> Then: >>> * is there a O(1) way to check if (= a b) ? >>> >>> In the above, we create two _different_ lists, both of which stores >>> [2 4 6], thus they're equal, but not identical >>> >>> Proposed solution: >>> tag the returned-value with a meta object, where the meta object >>> describes how the object was computed. >>> >>> in this case, both [2 4 6] would have _identical_ meta objects, >>> since they're both from the list [1 2 3] >>> >>> >>> >>> >>> >>> On Mon, Feb 24, 2014 at 5:56 PM, Brian Craft <craft...@gmail.com> >>> wrote: >>> > You're solving a similar problem, but I'm asking specifically about >>> using >>> > object identity, not equality, for tracking changes in a nested >>> structure. >>> > >>> > >>> > On Monday, February 24, 2014 1:42:03 PM UTC-8, t x wrote: >>> >> >>> >> I finally have a chance to give back. :-) >>> >> >>> >> I hacked up a newb's half-React. It does tree diffing + dom updating, >>> >> but not the virtual event handling system. >>> >> >>> >> I ran into this exact problem, and solved it as follows: >>> >> >>> >> >>> >> ## problem definition: >>> >> >>> >> render: data -> dom >>> >> tree-diff: dom * dom -> list of dom-update-actions >>> >> >>> >> we want to avoid "deep tree diffing" on tree-diff >>> >> >>> >> the key idea is as follows: >>> >> >>> >> when render is called twice with same args, >>> >> >>> >> * we still have to recompute every time >>> >> * we don't have to re-compare every time >>> >> >>> >> if render is a pure function, we do somethign like: >>> >> >>> >> (defn render [data] >>> >> (with-meta (render-pure data) {:pure data})) >>> >> >>> >> (render-pure data) gives us a dom-tree >>> >> we tag it with a meta project, telling us that it came from "data", >>> >> and that it was a pure function >>> >> >>> >> >>> >> then, doing the tree-diff stage, we do: >>> >> >>> >> >>> >> (defn tree-diff [old-dom new-dom] >>> >> (let [mo (meta old-dom) >>> >> no (meta new-dom)] >>> >> (if (and (:pure mo) (:pure no) (= (:pure mo) (:pure no))) >>> >> .. ah, they're from the same pure function, thus the same ... >>> >> ... okay, let's do expensive deep diff))) >>> >> >>> >> >>> >> so basically, we abuse meta objects, record >>> >> >>> >> * what data gave us this dom tree ? >>> >> * was the func that gave us the dom tree pure ? >>> >> >>> >> And if so, we just do a equality check on the data -- which are are >>> >> _not_ "regenerating" and thus matches an equality check. >>> >> >>> >> >>> >> Please let me if: >>> >> >>> >> (a) this resolves the issue >>> >> (b) I completely misunderstood the question >>> >> >>> >> >>> >> Thanks! >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> >>> >> On Mon, Feb 24, 2014 at 1:00 PM, Brian Craft <craft...@gmail.com> >>> wrote: >>> >> > This is vaguely related to David's posts about om/react, where he >>> talks >>> >> > about optimizing state change tracking by checking object identity >>> on >>> >> > immutable objects: deep compares can be avoided if same identity >>> implies >>> >> > no >>> >> > changes. >>> >> > >>> >> > My first thought was that there are many algorithms that will give >>> you a >>> >> > new >>> >> > object every time, even if nothing has changed. E.g. if your state >>> has >>> >> > an >>> >> > array whose elements must be validated, doing a map over the >>> elements >>> >> > will >>> >> > give you a new array every time, even if it makes no changes. >>> >> > >>> >> > Enforcing non-negative values, for instance: >>> >> > >>> >> > => (let [x {:a [1 -2 3]}] (update-in x [:a] (fn [y] (mapv #(if (< % >>> 0) 0 >>> >> > %) >>> >> > y)))) >>> >> > {:a [1 0 3]} >>> >> > >>> >> > In the following case the values are already non-negative, but we >>> still >>> >> > get >>> >> > a new object: >>> >> > >>> >> > => (let [x {:a [1 2 3]}] (identical? x (update-in x [:a] (fn [y] >>> (mapv >>> >> > #(if >>> >> > (< % 0) 0 %) y))))) >>> >> > false >>> >> > >>> >> > One can imagine trying to rewrite this so it passes through the >>> vector >>> >> > if >>> >> > nothing has changed. E.g. >>> >> > >>> >> > => (let [x {:a [1 2 3]}] (identical? x (update-in x [:a] (fn [y] >>> (reduce >>> >> > (fn >>> >> > [v i] (if (< (v i) 0) (assoc v i 0) v)) y (range (count y))))))) >>> >> > true >>> >> > >>> >> > => (let [x {:a [1 -1 3]}] (identical? x (update-in x [:a] (fn [y] >>> >> > (reduce >>> >> > (fn [v i] (if (< (v i) 0) (assoc v i 0) v)) y (range (count >>> y))))))) >>> >> > false >>> >> > >>> >> > I expect many algorithms would need to be reworked like this in >>> order to >>> >> > rely on object identity for change tracking. Is this madness? Am I >>> >> > thinking >>> >> > about this the wrong way? >>> >> > >>> >> > >>> >> > An interesting note here is that the next-to-last update-in, above, >>> >> > returned >>> >> > the same object. I didn't know update-in could return the same >>> object. A >>> >> > simpler example: >>> >> > >>> >> > => (let [x {"a" [1 2 3]} y (update-in x ["a"] (fn [z] z))] [x y >>> >> > (identical? >>> >> > x y)]) >>> >> > [{"a" [1 2 3]} {"a" [1 2 3]} true] >>> >> > >>> >> > => (let [x {"a" [1 2 3]} y (update-in x ["a"] (fn [z] [1 2 3]))] [x >>> y >>> >> > (identical? x y)]) >>> >> > [{"a" [1 2 3]} {"a" [1 2 3]} false] >>> >> > >>> >> > >>> >> > Is this some kind of optimization in update-in, that it doesn't >>> create a >>> >> > new >>> >> > object if the new attribute is identical to the old attribute? Is >>> it >>> >> > peculiar to the data type? Is it documented anywhere? >>> >> > >>> >> > >>> >> > -- >>> >> > You received this message because you are subscribed to the Google >>> >> > Groups "Clojure" group. >>> >> > To post to this group, send email to clo...@googlegroups.com >>> >> > Note that posts from new members are moderated - please be patient >>> with >>> >> > your >>> >> > first post. >>> >> > To unsubscribe from this group, send email to >>> >> > clojure+u...@googlegroups.com >>> >> > For more options, visit this group at >>> >> > http://groups.google.com/group/clojure?hl=en >>> >> > --- >>> >> > You received this message because you are subscribed to the Google >>> >> > Groups >>> >> > "Clojure" group. >>> >> > To unsubscribe from this group and stop receiving emails from it, >>> send >>> >> > an >>> >> > email to clojure+u...@googlegroups.com. >>> >> > For more options, visit https://groups.google.com/groups/opt_out. >>> > >>> > -- >>> > You received this message because you are subscribed to the Google >>> > Groups "Clojure" group. >>> > To post to this group, send email to clo...@googlegroups.com >>> > Note that posts from new members are moderated - please be patient >>> with your >>> > first post. >>> > To unsubscribe from this group, send email to >>> > clojure+u...@googlegroups.com >>> > For more options, visit this group at >>> > http://groups.google.com/group/clojure?hl=en >>> > --- >>> > You received this message because you are subscribed to the Google >>> Groups >>> > "Clojure" group. >>> > To unsubscribe from this group and stop receiving emails from it, send >>> an >>> > email to clojure+u...@googlegroups.com. >>> > For more options, visit https://groups.google.com/groups/opt_out. >>> >> -- >> You received this message because you are subscribed to the Google >> Groups "Clojure" group. >> To post to this group, send email to clo...@googlegroups.com<javascript:> >> Note that posts from new members are moderated - please be patient with >> your first post. >> To unsubscribe from this group, send email to >> clojure+u...@googlegroups.com <javascript:> >> For more options, visit this group at >> http://groups.google.com/group/clojure?hl=en >> --- >> You received this message because you are subscribed to the Google Groups >> "Clojure" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to clojure+u...@googlegroups.com <javascript:>. >> For more options, visit https://groups.google.com/groups/opt_out. >> > > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.