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
<[email protected]> 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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.