-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add A Law For Eq Which Ensures Consistency With Universal Equality #4118
Conversation
This commit adds a law `sameAsUniversalEquals` which ensures that the result of `Eq.eqv` is the same for `==` for any type which implements `Eq`. This similar to the `Hash` law `sameAsUniversalHash`. I can't think of any instance where we wouldn't want to ensure this behavior for a lawful `Eq` instance. A good sign is that all the cats tests pass without any changes here. My motivation for this change is I ran into a lawful `Eq` instance for a type in the wild recently, which did not agree with `==`. This was a bug, but wasn't caught by the law tests for the type.
Mandatory discord thread 😉 |
I don't think we should have this law. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about java.net.URL
, whose universal equality infamously does a network call?
What if we wanted to implement the URI comparison ladder? Non-canonical instances would be out, so this would now require newtypes.
Hmm...so I think I follow @djspiewak's point, but I worry about the legacy importance of I'm borrowing this example from @zarthross, but consider some opaque type for a case-insensitive string based on @rossabaker I'd be interested in you expanding on that a bit. Are you suggesting that we might have two uris, e.g. "http://localhost:80" and "http://localhost" which under the URI comparison ladder should be equivalent even though their raw representation is different causing a useful divergence between |
I would argue that it is the collections that are wrong. :) i.e. we should work towards having a typeclass based implementation of HashSet, SortedSet, HashMap, SortedMap that use only typeclasses, not the instance methods. |
Those are equivalent under 6.2.3 of the RFC, but not under the five stricter definitions before it. Universal equality needs to pick a winner, but we could provide six lawful instances as laid out in the RFC. Non-canonical instances are controversial, but the alternative is a newtype. Depending on the encoding, this may or may not be consistent with universal equality. This is not terribly different your the String example. |
@johnynek I completely agree with you about the collections being wrong. Despite that, I still worry about how practical it would be to have types such as that on the JVM, however if one controlled the usage domain carefully then I suppose it wouldn't be impossible. I'm going to close this PR. You've convinced me this shouldn't be a law, even if most types want this behavior in general. Thanks @rossabaker @johnynek and @armanbilge for the discussion and review! |
I agree it is an opportunity for bugs when we conflate Eq with ==. But if we had the law, why should we have the typeclass? Just use equals right? |
On this note, should we deprecate the |
I think that's an undesirable law for the same reasons, but any laws we repeal could violate assumptions made in code while the law was in effect. It would in some sense be a breaking change. |
Ah, that's right. So, we can't remove the test backwards-compatibly, but we can deprecate the law so that |
I'm not sure what "deprecating a law" means. Is that a documentation thing? |
A law is implemented as a method, so we could just slap a |
can we find anyone using Hash is pretty hard to test: you want something that is hard to test for, namely collisions should be rare. But collisions have to happen for anything with cardinality > 2^32 and usually much before that. So, when do you have too many collisions? Ideally, you want the law to prevent hashing everything to 0, because that will make the assumptions of hashed datastructures wrong, but how do you distinguish that from just getting really unlucky? I guess we could have a law like: That said, that law would forbid hashing Unit. So, we probably need something more careful. I would say we should stop testing the law because it makes the typeclass almost useless: if you want the hash just call .hashCode. BTW: I think it is a reasonably law to put on some things: Strings, primitives, tuples and case classes of these recursively. Those hashes are fine. But putting it on all things isn't great. |
Yeah, I don't know how to make a "hash doesn't suck" law without knowing the cardinality of the type. It could be an interesting property outside the typeclass. The practical matter of ruling out |
Maybe a silly idea, but what if we introduce a |
Since you mentioned |
This commit adds a law
sameAsUniversalEquals
which ensures that the result ofEq.eqv
is the same for==
for any type which implementsEq
. This similar to theHash
lawsameAsUniversalHash
.I can't think of any instance where we wouldn't want to ensure this behavior for a lawful
Eq
instance. A good sign is that all the cats tests pass without any changes here.My motivation for this change is I ran into a lawful
Eq
instance for a type in the wild recently, which did not agree with==
. This was a bug, but wasn't caught by the law tests for the type.