Skip to content
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

Comparisons of structs with == lower suprisingly to === and documenation doesn't make it clear #51133

Open
freemin7 opened this issue Aug 31, 2023 · 3 comments
Labels
docs This change adds or pertains to documentation equality Issues relating to equality relations: ==, ===, isequal

Comments

@freemin7
Copy link
Contributor

freemin7 commented Aug 31, 2023

versioninfo()
Julia Version 1.8.5
Commit 17cfb8e65ea (2023-01-08 06:45 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 8 × 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, tigerlake)
  Threads: 1 on 8 virtual cores

installed via JuliaUp

using UUIDs

struct flower
    car::Vector{UUID}
    house::UUID
end

 uidv = [UUIDs.uuid4(), UUIDs.uuid4(), UUIDs.uuid4()]
 uid = UUIDs.uuid4()
 
 lettuce = flower(uidv, uid)


@assert typeof(lettuce) == typeof(eval( Meta.parse( string(flower(uidv, uid))))) ## passes
@assert lettuce.car == eval( Meta.parse( string(flower(uidv, uid)))).car ## passes
@assert typeof(lettuce.car) == typeof(eval( Meta.parse( string(flower(uidv, uid)))).car) ## passes
@assert lettuce.house == eval( Meta.parse( string(flower(uidv, uid)))).house ## passes
@assert typeof(lettuce.house) == typeof(eval( Meta.parse( string(flower(uidv, uid)))).house) ## passes
@assert fieldcount(flower) == 2
@assert string(lettuce) == string(eval( Meta.parse( string(flower(uidv, uid))))) ## passess

@assert hash(lettuce.house) == hash(eval( Meta.parse( string(flower(uidv, uid)))).house) ## passes
@assert hash(lettuce.car) == hash(eval( Meta.parse( string(flower(uidv, uid)))).car) ## passes
@assert flower(uidv, uid) == flower(uidv, uid)


@assert flower(uidv, uid) == eval(:(flower(uidv, uid)))


## So they should be identical in every way but
## both comparisons below fail
@assert lettuce == eval( Meta.parse( string(flower(uidv, uid)))) 
@assert hash(lettuce) == hash(eval( Meta.parse( string(flower(uidv, uid))))) 

Context: I discovered this problem using JSON3 where deserialisation/serialisation identities failed however could reproduce the issue in without those packages loaded. The same holds for uuid1. This is extremely cursed.

Some more testing yielded that:

cornflakes = string(flower(uidv, uid))
@assert cornflakes == string(flower(uidv, uid)) ## passes
soap = Meta.parse(cornflakes)
@assert soap == Meta.parse(cornflakes) ## passes

@assert eval(soap) == eval(soap) ## Fails

## this also fails
@assert eval(:(flower(UUID[UUID("c473d632-4815-11ee-047b-53ba6f201cba"), UUID("c473d644-4815-11ee-2bf5-01764c9f2b45"), UUID("c473d658-4815-11ee-361c-3d983a147777")], UUID("c4745718-4815-11ee-1ec9-fb22fba57306")))) == eval(:(flower(UUID[UUID("c473d632-4815-11ee-047b-53ba6f201cba"), UUID("c473d644-4815-11ee-2bf5-01764c9f2b45"), UUID("c473d658-4815-11ee-361c-3d983a147777")], UUID("c4745718-4815-11ee-1ec9-fb22fba57306"))))

## while those two pass
@assert eval(:(UUID[UUID("c473d632-4815-11ee-047b-53ba6f201cba"), UUID("c473d644-4815-11ee-2bf5-01764c9f2b45"), UUID("c473d658-4815-11ee-361c-3d983a147777")])) == eval(:(UUID[UUID("c473d632-4815-11ee-047b-53ba6f201cba"), UUID("c473d644-4815-11ee-2bf5-01764c9f2b45"), UUID("c473d658-4815-11ee-361c-3d983a147777")]))

@assert eval(:(UUID("c473d658-4815-11ee-361c-3d983a147777"))) == eval(:(UUID("c473d658-4815-11ee-361c-3d983a147777")))
@vtjnash
Copy link
Member

vtjnash commented Aug 31, 2023

I don't see the issue. There are two different arrays (in the car field), and they are not ===, so they are distinctly different flower structs with different == and hash. That seems to be what you are seeing too, as I would expect

@vtjnash vtjnash added the needs more info Clarification or a reproducible example is required label Aug 31, 2023
@jakobnissen
Copy link
Contributor

@freemin7 To expand on this a little, your issue is equivalent to this:

julia> struct Foo
           x::Vector{Int}
       end

julia> Foo([1]) == Foo([1])
false

This happens because == is not explicitly defined for Foo, so it falls back to the following fallback definition

==(x, y) = x === y

And since the two inner arrays are not === to each other, the result is false.

The === fallback is unfortunate. Issues #4648 and #40717 propose alternative (and IMO, better) solutions, but changing the default fallback is breaking, so it will probably never happen.

@freemin7 freemin7 changed the title struct of UUIDs fails comparisons to itself Comparisons of structs with == lower suprisingly to === and documenation doesn't make it clear Aug 31, 2023
@freemin7
Copy link
Contributor Author

freemin7 commented Aug 31, 2023

Well it's not what i expected. I thought i tested that case with.

@assert lettuce.car == eval( Meta.parse( string(flower(uidv, uid)))).car ## passes

It is extremely surprising behavior that

@assert ( Foo([1]).x == Foo([1]).x ) && not(Foo([1]) == Foo([1]))

And the documentation while truthful is only truthful by omission. When i work with UUIDs and serialization i think "someone might have messed up the interning" https://en.wikipedia.org/wiki/Interning_(computer_science) of UUIDs and while i read "Falls back to ===." in ?== that wasn't strong enough language to consider that it a sensible default wasn't chosen for structs. The "For example," makes the listing of exception to the fallback incomplete. "structs being a direct sub-type of Any use the fallback by default." would have been sufficiently explicit to grab my attention.

Another alternative would be a referencing a struct_equal #4648 (comment) in a see also section if something like that existed in stdlib. Does something like that exist in Base already?

Either way the issue could be closed by a more explicit documentation.

@jakobnissen jakobnissen added docs This change adds or pertains to documentation and removed needs more info Clarification or a reproducible example is required labels Aug 31, 2023
@nsajko nsajko added the equality Issues relating to equality relations: ==, ===, isequal label Dec 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This change adds or pertains to documentation equality Issues relating to equality relations: ==, ===, isequal
Projects
None yet
Development

No branches or pull requests

4 participants