Simplest way to define an efficient `==` on a new type

Because of the issues with the default definition of ==, I often find myself having to define == to be extra safe. It would be nice if writing the function were really simple, I’d like to do

typeof(t1) == typeof(t2) || (return false)
all(getfield(t1, i) == getfield(t2, i) for i ∈ 1:nfields(t1))

Now, initially I would have thought that I can’t do this, because the compiler would not know ahead of time what all the types of the fields would be and I’d get lousy performance. However, I ran @code_warntype on this and lo and behold it looks completely type stable.

Can I trust this in the general case? It seems a little too magical.

Haven’t checked it myself, but I think code_warntype is not looking deep enough to see the instability. The part that’s going to be unstable is gonna be the anonymous function that the generator produces. The return values there, though, are stable (just Bools), so it doesn’t show up in the outer function.

That’s pretty much what I was thinking. So I suppose in that case the general advice would be to explicitly check each field? It would be really nice if there were a macro in Base for defining these functions for as long as this is not the default definition.

It would be really nice if there were a macro in Base for defining these functions for as long as this is not the default definition.

There’s

2 Likes

Nice. (Though I would like to point out that I believe this is one of the rare instances where putting this in Base would be well justified).)

By the way, this makes a huge difference:

julia> @btime t1 == t2
  58.533 ns (2 allocations: 64 bytes)
false

julia> @btime t1 ≐ t2
  3.097 ns (0 allocations: 0 bytes)
false

where was defined explicitly and == was defined as I stated above.