[swift-evolution] [pitch] Comparison Reform

Dave Abrahams dabrahams at apple.com
Sun Apr 23 06:35:27 CDT 2017


on Sat Apr 22 2017, Chris Lattner <clattner-AT-nondot.org> wrote:

> On Apr 22, 2017, at 6:06 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>> but my quick reaction to `&==` is that it would make me quite
>> nervous to have `==` not bound to 754-equals as it is in essentially
>> every other language. In particular, I worry about the risk of
>> people porting numerical code that depends on isnan(x) <—> !(x < y)
>> in non-obvious ways that they are unlikely to test. I’ll try to
>
>> follow up with more detailed thoughts tomorrow.
>> 
>> Indeed, it makes me a little nervous too. That said, `==` being
>> either bound or not bound to 754 depending on the context is what
>> makes me even more nervous.
>> 
>> I was once adamantly against a new spelling for `==`, but on
>> reconsideration it's clear to me that few if any numerical recipes
>> can be ported verbatim from C-like languages and we should probably
>> not encourage people to do so. Already, `+` needs to be rewritten as
>> `&+`, `<<` probably should be rewritten as `&<<` (I still haven't
>> had enough time to think about this), and the bitwise operators have
>> differing precedences that require careful proofreading.
>
> I haven’t been following this proposal or discussion closely, but it
> seems to me that there are a few workable approaches with different
> tradeoffs:

Hey Chris, thanks for chiming in.  This is interesting, but I need a
bit more information to be able to evaluate it:

> 1. The strictly correct but user hostile approach:
>
> * == and != are tied to the Equatable protocol, which is essentially the == operation.

Whose semantics requirements are...?

> * <, <=, >, >= are tied to the Comparable protocol, which is essentially the <=> operation.

Whose semantics requirements are...?

> * Hashable doesn’t require equatable, it requires a related StrictlyEquatable protocol.

Whose semantics requirements are...?

> * StrictlyEquatable refines Equatable (with no other requirements, it
> is just a marker protocol), 

Marker for what?  Surely there are stronger semantic requirements?
IMO there are probablby useful ways to introduce tighter requirements,
e.g. by forcing an optional-returning method to non-optional (roughly
Xiaodi's idea).

> in which case FP types can’t conform to it, and thus can’t participate
> as dictionary keys
>
> => This approach sucks because you can’t have Set<Float>, or Dictionary<Float, String>.
>
> 2. The strictly correct but somewhat user hostile approach:
>
> * == and != are tied to the Equatable protocol, which is essentially the == operation.
> * <, <=, >, >= are tied to the Comparable protocol, which is essentially the <=> operation.
> * Hashable doesn’t require equatable, it requires a related StrictlyEquatable protocol.
> * StrictlyEquatable doesn’t refine Equatable: it has a different
> requirement, and FP types can therefore implement both Equatable and
> StrictlyEquatable.

Again, I want to understand the semantic requirements of these
protocols, and also how they impact the documentation of generic
algorithms.

> => This approach is suboptimal because implementing your own type
> requires you to implement the <=> operation, as well as the
> StrictlyEquatable protocol, both.
>
> 3. The user friendly but incorrect model:
>
> * == and != are tied to the Equatable protocol, which is essentially the == operation.
> * <, <=, >, >= are tied to the Comparable protocol, which is essentially the <=> operation.
> * Hashable is defined in terms of Equatable.
>
> => This is easy (types just have to define <=>), but fails for FP types.

How so, specifically?  I'm not sure how you think these operations
would behave for FP.

> I don’t think that this proposal is acceptable as written.  

I wouldn't be happy with accepting it in this form either.  Discussions
have raised some serious questions for which I don't think we have clear
answers.  However, there is yet hope that we will find them here :-)

> I think it is really bad that abstracting a concrete algorithm would
> change its behavior so substantially.  I don’t care about SNaNs, but I
> do care about the difference between +0/-1

That reads like you're saying you want +0.0 == -0.0 to be false, but I
suspect what you really mean is not that there should be no difference
in the result +0.0 == -0.0 regardless of context.  Am I reading that
correctly?

Making +0.0 == -0.0 true in all contexts would be an easy change to the
proposal and probably the right move, but I think there are other
serious issues that I'm not sure we've solved yet.

> and secondarily that of NaN handling.  It seems really bad that
> generalizing something like:
>
> func doThing(a : Double, b : Double) -> Bool {
>    ….
>    return a != b
> }
>
> to:
>
> func doThing<T : FloatingPoint> (a : T, b : T) -> Bool {
>    ….
>    return a != b
> }
>
> would change behavior (e.g. when a is -0.0 and b is +0.0).   

Again, it wouldn't...

> Likewise, "T : Equatable".

...but that one would.

-- 
-Dave


More information about the swift-evolution mailing list