Open soronpo opened 5 years ago
I like this approach. It would also allow us to remove equality from types by providing ambiguous implicits. For those situations where you absolutely require the axiom of choice to be a genuine choice.
It's a very intriguing thought, which might just work. Let's see:
Would things break if ==
and !=
were extension methods instead of methods of Any
? I don't see anything at the moment. That's something we should be able to test quickly in isolation of the rest of the proposal.
The two fallback extension methods would not be available under -language.strictEquality
. That can be arranged.
If a type defines its own extension methods !=
and ==
these will win against the fallback since they have more specific types.
The only additional magic we'd need is for the situation where we do not have strictEquality
set. Under multiversal equality, there's a subtle set of rules that determines whether we can use the fallback eqAny
instance. In
(x: S) == (y: T)
the fallback can be used if one of the following is true:
==
extension method (or rather, implements the equality class)These rules were developed because they give us good backwards compatibility and useful checking capabilities at the same time. It seems to me that these rules can be applied regardless of whether the fallbacks are members of Any or extension methods.
So, yes, definitely worth exploring further!
I don't know much about binary compatibility and the extended methods backend. Dotty wants to share binaries with Scala 2. Is implementing a ==
as an extension instead of an internal class definition binary compatible?
So there's two directions of interroperability here that I personally think are relevant. Using scala2 compiled code in scala3 source code, and using the scala3 compiled code in java source code. I would expect that interop for the scala2->scala3 case could be made automatic, by faking in == and != methods directly on those types when they are imported into the scala3 universe. Those methods would take prescedence over those supplied by extension methods.
The Java case is the one I'm not so sure of. That would require some boilerplate on the scala3 class files to bind .equals to the appropriate equality instance. Perhaps that's not a problem in reality, as java code won't very often need to rely upon scala's notion of equality.
For a moment I thought I found an example of something that could break, if there is a class that overrides def == (that : Any)
, but fortunately this definition is final
.
FWIW, another benefit is that an IDE like IntelliJ error highlighting works better with missing extensions. So if we remove ==
from Any
then two classes that cannot equal will get a red-squigly under ==
.
@soronpo Been pointed to this issue by @AleksanderBG about #5810. I wonder if this would make a difference? But I suspect not, because that depends on rules for fallback (that could stay unchanged).
If I'm reading the issue correctly, the current proposal is to:
==
defined as extension method w/o checking for Eq
==
as an extension method on Any
language.strictEquality
If the above is correct, then #5810 is actually relevant here - we wanted to be able to accept code like:
def f[T](x: T) =
if (x == null) ...
else if (x == "abc") ...
else ...
which we would not be able to if ==
was unavailable on Any
.
BTW, is the plan for strictEquality
to be opt-out instead of opt-in in the future?
@soronpo Yes - it's a language import (in the scala.language
sense).
@AleksanderBG I think we need something more complex than you describe, whatever we choose for #5810. We'd have to figure rules equivalent to today (with/without #5810).
Here's what happens with the rules @AleksanderBG describes: Without ==
on Any
under strictEquality
, we'd reject that code and regress without noticing, since we lack that test under git grep -l strictEquality tests
. That issue asks to intentionally break that code under strictEquality
, and require (x: Any) == "abc"
(which is supported today). But if we drop the Any
altogether, (x: Any) == "abc"
would break too.
BTW, is the plan for
strictEquality
to be opt-out instead of opt-in in the future?@soronpo Yes - it's a language import (in the
scala.language
sense).
@AleksanderBG Not sure that's the question — language imports are opt-in. FWIW, @OlivierBlanvillain proposed looseEquality
time ago but that hasn't happened yet.
Conversely, https://github.com/lampepfl/dotty/issues/1247#issuecomment-218508986 says it's not clear how important it'd be to move to strictEquality
.
Right, I read @soronpo's message the other way around. As far as I am aware, there are no plans to change the current approach to "loose" (permissive?) equality being the default.
How do we advance on this?
Is there time to make it available on 3.0?
I can try to implement the simple stuff (remove the public ==
and !=
from the class and create the extension methods), but I have no idea where to implement the "magic".
I would love it if typeclasses will be able to use ==
and !=
instead of ===
and =!=
.
I think old code with ==
and !=
needs to be rewritten to some part because the new multiversal equality is more constrained than equals?
I remember having looked at it before and then noticing that simply going to extension methods would not allow abstraction. E.g., you need a type class to define a safe version of contains
. Extension methods alone don't help you there.
It might still be that extension methods would be a win, but our design space for 3.0 is very constrained by now:
That said, I would find it interesting to see the results of a large scale exploration what things would look like with extension methods. It will probably end up orthogonal to the question of constraining equality with a type class. Or maybe it will turn out that it's the type class that will contain the extension methods. It's interesting but also scary since equality is so pervasive and everything has to continue to work like it does now.
Dotty has advanced us to Multiversal Equality, which is great, but I feel can be better now that we have Extension Methods in the language.
Problem: Say I wish to use the
==
operator to extend a class with a new comparison.Currently we get the error
Values of types Foo and Bar cannot be compared with == or !=
Solution: If the
==
and!=
definitions are removed (fromAny
?) and implemented as extension methods, then the above code will run as expected.Bonus feature: Once
==
and!=
are extension methods, then DSLs can finally use them to return types other than Boolean. For example, while it was always possible to do:It did not allow to define commutative comparison:
Implicit (extension) classes could not bypass the default
==
, for the same reason extension methods currently can't.