There are likely to be common ways of writing data transforms that happen
to return the same identical object as they were given, because
'primitives' like assoc often do.

I don't know of anyone who intentionally relies upon this behavior for
correctness of their code, and would strongly advise against ever doing so.

I also don't know of anyone who intentionally tries to write such code in
order to improve performance of their Clojure programs.  If they did, I
would guess they would often be frustrated by small changes to their
program that reduced performance by no longer returning identical objects.

Andy


On Wed, Feb 26, 2014 at 11:11 AM, Brian Craft <craft.br...@gmail.com> wrote:

> 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> 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
>>> 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 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.
>

-- 
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.

Reply via email to