Hi Andy,

Having thunk on it, I will try to articulate my preference for nil
over {} more clearly:

(clojure.data/diff {:x {:y 1}} {:x {}})
;; => ({:x {:y 1}} {:x nil} nil)

Do you agree the current result is wrong?

:x nil is not 'in' {:x {}}
nil is not associative so in this context does not mean empty, it
means literally nil.
only (clojure.data/diff {:x {:y 1}} {:x nil}) should return {:x nil}
as the 'only in b' result.

So choosing between ({:x {:y 1}} nil nil)  vs  ({:x {:y 1}} {:x {}}
nil) as solutions:

I argue that using {} adds an unnecessary special case.

Non-empty absence uses nil to indicate nothing was added.
(clojure.data/diff {:x {:y 1, :z 2}} {:x {:z 2}})
;; => ({:x {:y 1}} nil {:x {:z 2}})

It is not correct to say that :x {} was added as a replacement value, because
(clojure.data/diff {:x {:z 2}} {:x {:y 1, :z 2}})
;; => (nil {:x {:y 1}} {:x {:z 2}})
{:y 1} is not a replacement, but is associative (there is {:y 1, :z 2} in both).

It is correct to say that :x {} equally represents no change, but
additionally flags the collection is now empty. For my part I do not
think clojure.data/diff should do this. It might be useful
information, but is easily identifiable state, and comes at the cost
of introducing a special case.

Also consider this case:
(clojure.data/diff {} {:x {}})
;; => (nil {:x {}} nil)
There was no map associated with :x in the first argument, but there
is in the second argument. Here it accurately represents differential
participation and so should be distinguishable from (clojure.data/diff
{:x {:y 1}} {:x {}}).

What do you think?



Regarding your questions about patchin specifically:

On Sat, Feb 21, 2015 at 4:36 PM, Andy Fingerhut
<andy.finger...@gmail.com> wrote:
> Do you plan to support sending a different 'diff' in each of these cases, 
> such that the 'receiver' can end up in the same final state, for each of 
> these case?

Yes, patchin works this way.


> Or perhaps the idea is that some of those are not supported?

I claim that every data representation/change that conforms to EDN is
currently supported :)
Thank you for the thoughtful test cases, I recognized an opportunity
to remove some redundancy in patch creation while experimenting with
them.


> both sides is {:x {:y 1}}, and the 'sender' wants to change to one of these 
> states:

Here is some REPL output of the patches generated which successfully
cause those transformations:

(a) patchin> (diff {:x {:y 1}} {:x nil})
;; => [{:x nil}]

(b) patchin> (diff {:x {:y 1}} {:x {}})
;; => [{:x {}}]

(bb) patchin> (diff {:x {:y 1}} {:x []})
;; => [{:x []}]

(c) patchin> (diff {:x {:y 1}} {:x {:y nil}})
;; => [{:x {:y nil}}]

All of these are 'replace the entire state with this', because the
equivalent dissoc/assoc steps create a larger patch than the final
state.


To demonstrate transformations, we need to add some extra data:

(a) patchin> (diff {:x {:y "22"}, :z "somelargedata"} {:x nil :z
"somelargedata"})
;; => [{} {:x nil}]

{} indicates nothing to dissoc/disj
{:x nil} are the things to replace.
so the second transform results in {:x nil :z "somelargedata"}

patchin> (patch {:x {:y "22"}, :z "somelargedata"} [{} {:x nil}])
;; => {:z "somelargedata", :x nil}


(b) patchin> (diff {:x {:y "22"}, :z "somelargedata"} {:x {} :z
"somelargedata"})
;; => [{:x {:y 1}} {}]

{:x {:y 1}} are the things to remove (1 is an arbitrary non-map/set terminal).
This indicates the paths to dissoc/disj; in this case there is only
one path [:x :y]
so the first transform results in: {:x {} :z "somelargedata"}
{} means nothing to add

patchin> (patch {:x {:y "22"}, :z "somelargedata"} [{:x {:y 1}} {}])
;; => {:z "somelargedata", :x {}}


(bb) patchin> (diff {:x {:y "22"}, :z "somelargedata"} {:x [] :z
"somelargedata"})
;; => [{} {:x []}]

Same as (a), :x is replaced with []

patchin> (patch {:x {:y "22"}, :z "somelargedata"} [{} {:x []}])
;; => {:z "somelargedata", :x []}


(c) patchin> (diff {:x {:y "22"}, :z "somelargedata"} {:x {:y nil} :z
"somelargedata"})
;; => [{} {:x {:y nil}}]

Replacing path [:x :y] with nil

patchin> (patch {:x {:y "22"}, :z "somelargedata"} [{} {:x {:y nil}}])
;; => {:z "somelargedata", :x {:y nil}}


The way that patchin identifies the difference between empty
collections and nil is by monkey patching data/diff-associative-key to
avoid the empty associative issue.

Thank you for your interest.


Regards,
Timothy

-- 
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/d/optout.

Reply via email to