-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Deprecate implicit universal Equiv instance #7164
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
Conversation
One of the benefits of type class-based equality is that the compiler can enforce that you only check equality on types for which it is meaningful (as opposed to universal equality such as Java's `equals` that will allow you to compare any two objects even if it's a meaningless comparison due to a typo). Having an implicit universal `Equiv` instance throws away this benefit. With this instance in scope (and it always is, since it's in the companion object), the compiler will happily let you compare two `Any` instances, two `Function1` instances, etc, even though doing so is probably a mistake. Also, even if you have defined a well-behaved `Equiv` instance for your type, if you forget to import it, the universal instance will jump in to take its place without the compiler warning you. [Here](https://twitter.com/jvican/status/1034529059911426048?s=19) is a recent tweet from @jvican on the subject. This also came up in a [discussion](typelevel/cats#2455) in the Cats repo that I started recently about what it would take for Cats to start using `Equiv`/`PartialOrdering`/`PartialOrder` from the standard library instead of having its own version of these type classes. I'm a bit hesitant to link to that conversation here, since it goes well beyond the scope of this PR, but it is relevant. If desired, I'll happily change this to a PR to remove this instance altogether for Scala 2.13, but I wasn't sure whether it was too late to introduce that change.
NthPortal
left a comment
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.
LGTM
jvican
left a comment
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.
LGTM, this change is for the better. 👍
|
Deprecating in 2.12.7 and removing entirely in 2.13.0-RC1 might not be out of the question. wdyt? |
|
@SethTisue sounds great to me. Who is the "y" in wdyt? :) |
|
I'm 100% in favour of removing the instance entirely in 2.13.0-RC1. I'm not so sure about deprecation, in any release. If something is deprecated there has to be some feasible remedial action the receiver of the deprecation message can take. In this case I don't see what that could be, other than "put up with it until you can migrate to 2.13.0". |
And there is -- they can use Of course, maybe they don't want to use it at all, rather than explicitly through |
This makes perfectly good sense for explicitly applied methods, but not for implicitly applied methods. An end user will not typically be using either explicitly. To use |
|
could we provide a universal instance that isn't deprecated, but isn't found by implicit search unless it is explicitly imported? |
surely the hypothetical user can just have a local I'm for deprecating this in 2.12.7 and having it completely gone in 2.13.0, by the way. |
that's less convenient than providing something importable, since not saying it's a big deal either way, just explaining |
Yes, if you know what |
|
A github search suggests that |
|
I would be in favour of putting it inside an |
|
Everyone seems to be in favor of removing this in its current form for Scala 2.13. Yay 🎉 I can do that. Now for my thoughts/observations on the more controversial pieces. Miles' GitHub search lines up with my experience that If someone were to want to recreate this, they would need to do something like the following (and then import it): object Implicits {
implicit def universalEquiv[A]: Equiv[A] = Equiv.universal
}There is some talk of adding something like this to the standard library. My initial reaction is hesitation to do this, because of a combination of the two following reasons:
I've mentored a number of people in Scala and a recurring pattern that I see is people new to Scala seeing stuff in the standard library (such as |
|
@ceedubs I agree with your analysis, thanks for writing that up 👍 To make the deprecation warning more actionable, could we describe the migration strategy in the deprecation message? Agreed that it can be long, but I think it's worth it 😄 In the message, I would also invite users to think if they really need universal equality in |
|
I still object to the deprecation on the
That's a negative with no upside that I can see. |
I think it's appropriate to ask users that really need the type to be inferred to inline the implicit definition in an object ehy own or, if they really want it enabled by default, in a package object (which in 2.12.x is still an implicit candidate). I wouldn't worry about |
I'm not worrying about that. I'm worrying about the fact that the deprecation warning on the trait will bug everyone building the scala/scala tree to no useful end. The fact that the trait isn't reusable means that these are the only people who will ever see the deprecation. |
| def equiv(x: T, y: T): Boolean | ||
| } | ||
|
|
||
| @deprecated("This only existed for the implicit universalEquiv. Use the explicit Equiv.universal instead.", "2.13.0") |
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.
As Miles says, given the self-type, this isn't useable anywhere outside of Equiv, so let's avoid the spam.
|
Oh I was missing the bit about the self type making it so that nobody else could have been extending the trait. That makes sense. Thanks for all of the good feedback, everyone. I'll close this out and soon I'll create 2 new PRs: one to remove these for 2.13 and one to deprecate the instance (with a more helpful error message) in 2.12. |
This targets the 2.12 branch. See discussion [here](scala#7164).
Based on discussion [here](scala#7164). I didn't seem to have to change any tests for this, so either it was untested or I'm missing something.
Based on discussion [here](scala#7164). I didn't seem to have to change any tests for this, so either it was untested or I'm missing something.
This targets the 2.12 branch. See discussion [here](scala#7164).
One of the benefits of type class-based equality is that the compiler
can enforce that you only check equality on types for which it is
meaningful (as opposed to universal equality such as Java's
equalsthat will allow you to compare any two objects even if it's a
meaningless comparison due to a typo).
Having an implicit universal
Equivinstance throws away this benefit.With this instance in scope (and it always is, since it's in the
companion object), the compiler will happily let you compare two
Anyinstances, two
Function1instances, etc, even though doing so isprobably a mistake. Also, even if you have defined a well-behaved
Equivinstance for your type, if you forget to import it, theuniversal instance will jump in to take its place without the compiler
warning you.
Here is a recent tweet from @jvican on the subject.
This also came up in a discussion in the Cats
repo that I started recently about what it would take for Cats to start
using
Equiv/PartialOrdering/Orderingfrom the standard libraryinstead of having its own version of these type classes. I'm a bit
hesitant to link to that conversation here, since it goes well beyond
the scope of this PR, but it is relevant.
If desired, I'll happily change this to a PR to remove this instance
altogether for Scala 2.13, but I wasn't sure whether it was too late to
introduce that change.