-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
== for immutables should recursively call == on its fields #4648
Comments
I would also argue that for mutable types maybe we should throw an error in the absence of specialized |
Tuples already do the right thing: julia> ("baz",) == ("baz",)
true |
Arrays also behave this way julia> {"abc",5} == {"abc",5}
true
julia> {"abc",5} == {"abc",4}
false I am not sure if I think it is a good thing to introduce more differences between mutable and immutable types, but a default implementation of |
I can agree that the current default |
If we throw an error if the comparison function is undefined, the code for comparing arbitrary objects will be longer. try
return a == b
catch
return false
end or applicable(==, a, b) && a == b |
Perhaps I'm missing something but why not immediately return true from these checks if the two objects are identical? If this is valid there are likely other places this could happen too...but I thought I would first check. Thanks, Glen
@StefanKarpinski, did this get implemented as part of the hashing changes? I seem to recall not needing |
Immutables already call |
But doesn't this definition mean it is? ==(x,y) = x === y |
That calls |
Ah, got it! |
I still think we should do this. All three equality predicates should probably call themselves recursively. |
Should |
Maybe a simple solution to this issue is to make |
Making |
I'm not a big fan of this change since I think we shouldn't try to guess what |
This seems to be close to a duplicate of #12198 (comment)? |
That's already fixed on master, but it's certainly a larger issue than string (e.g. BigInts). |
That's done --- we now have |
Fixing only the examples in this thread is not at all what this issue is about. |
The trait idea is orthogonal; the question would remain as to what the default behavior should be in the absence of trait declarations for a new type. |
Sorry for not having followed up on my promise, life caught up... I don't think I will have time to put work into this just now. @tpapp, you're welcome to work on the traits-stuff if you fancy. Anyway, now that I've spent some time thinking about this, my 2-cents:
Thus, this leaves the two solutions:
|
Do you mean equality should be recursive?
That's not the reason. The reason is that immutable values with the same contents cannot be distinguished, while mutable values with the same contents can be distinguished by mutating one and seeing if the "other" one also changes. |
This is probably a miss-understanding. I mean that the following is not desirable:
Thus I think "field-by-field comparison" describes a system where
But if I care about this I should use |
Ah, I see now. I don't think anybody has proposed this. We have been talking about making |
A minimally invasive yet reasonably convenient extension of the status quo could be the following:
This would preserve existing behavior, avoid spurious equality when not intended (eg making a This would also not be a breaking change, and could be done later. Still, if this is acceptable, I would make a try at this, I would love to see this in |
Triage prefers the following solution, which was spelt out above in parts, but not in one place
This is basically what AutoHashEqual does, except that it seems slightly better |
I just remembered the original issue calls for doing this for immutable types, but it seems we're leaning towards using it for everything? That brings up (1) object cycles, (2) strange default behavior for things like Pipes. The way I see it, there are thousands of julia types out there, call the number So it was not discussed (much?) on the call, but do we want to limit this to immutables? |
Pondering this some more after the call, I'm thinking that the current default of not-equal has been a much better default than field-equality (if we're going to define equals at all). For example, it's seems odd that we'll now need to define It seems like there's probably many other cases like that too, such as |
I would continue to favor doing this for mutable types, though I feel less strongly about that. I'm not really concerned about getting StackOverflowErrors for circular references by default, since e.g. the same happens here:
|
But it can cause stack overflows in cases where people were relying on the |
I think this is still controversial and doesn't need to be done right now. Moving to 2.0. |
It might be handy to at least have structural equality as a standard library function that can be used for user-defined types. For example, I've been using this generated function for most of my types to avoid manual equality definition: # ASSUMTION: `e1` and `e2` have the same run-time type
@generated structEqual(e1, e2) = begin
if fieldcount(e1) == 0
return :(true)
end
mkEq = fldName -> :(e1.$fldName == e2.$fldName)
# generate individual equality checks
eqExprs = map(mkEq, fieldnames(e1))
# construct &&-expression for chaining all checks
mkAnd = (expr, acc) -> Expr(:&&, expr, acc)
# no need in initial accumulator because eqExprs is not empty
foldr(mkAnd, eqExprs)
end And then use it to define equality: Base.:(==)(x :: MyType, y :: MyType) = structEqual(x, y) |
awesome idea! I included it into a related package I myself build, and extended it to similar cases. |
@schlichtanders: I like that you exposed the function versions as part of the API. My problem with macros like |
This doesn't make much sense:
If the fields are
==
to each other then the objects should be==
to each other.The text was updated successfully, but these errors were encountered: