> On Oct 31, 2017, at 09:07, Stephen Canon via swift-dev <swift-dev@swift.org> 
> wrote:
> 
> [Replying to the thread as a whole]
> 
> There have been a bunch of suggestions for variants of `==` that either trap 
> on NaN or return `Bool?`. I think that these suggestions result from people 
> getting tunnel-vision on the idea of “make FloatingPoint equality satisfy 
> desired axioms of Equatable / Comparable”. This is misguided. Our goal is 
> (should be) to make a language usable by developers; satisfying axioms is 
> only useful in as much as they serve that goal.
> 
> Trapping or returning `Bool?` does not make it easier to write correct 
> concrete code, and it does not enable writing generic algorithms that operate 
> on Comparable or Equatable. Those are the problems to be solved.
> 
> Why do they not help write correct concrete code? The overwhelming majority 
> of cases in which IEEE 754 semantics lead to bugs are due to non-reflexivity 
> of equality, so let’s focus on that. In the cases where this causes a bug, 
> the user has code that looks like this:
> 
>    // Programmer fails to consider NaN behavior.
>    if a == b {
>    }
> 
> but the correct implementation would be:
> 
>    // Programmer has thought about how to handle NaN here.
>    if a == b || (a.isNaN && b.isNaN) {
>    }
> 
> W.r.t ease of writing correct *concrete* code, the task is to make *this* 
> specific case cleaner and more intuitive. What does this look like under 
> other proposed notions of equality? Suppose we make comparisons with NaN trap:
> 
>    // Programmer fails to consider NaN behavior.  This now traps if a or b is 
> NaN.
>    // That’s somewhat safer, but almost surely not the desired behavior.
>    if a == b {
>    }
> 
>    // Programmer considers NaNs. They now cannot use `==` until they rule out
>    // either a or b is NaN. This actually makes the code *more* complicated 
> and
>    // less readable. Alternatively, they use `&==` or whatever we call the 
> unsafe
>    // comparison and it’s just like what we had before, except now they have a
>    // “weird operator”.
>    if (!a.isNaN && !b.isNaN && a == b) || (a.isNaN && b.isNaN) {
>    }
> 
> Now what happens if we return Bool?
> 
>    // Programmer fails to consider NaN behavior.  Maybe the error when they
>    // wrote a == b clues them in that they should. Otherwise they just throw 
> in
>    // a `!` and move on. They have the same bug they had before.
>    if (a == b)! {
>    }
> 
>    // Programmer considers NaNs. Unchanged from what we have currently,
>    // except that we replace || with ??.
>    if a == b ?? (a.isNaN && b.isNaN) {
>    }
> 
> If we are going to do the work of introducing another notion of 
> floating-point equality, it should directly solve non-reflexivity of equality 
> *by making equality reflexive*. My preferred approach would be to simply 
> identify all NaNs:
> 
>    // Programmer fails to consider NaN behavior. Now their code works!
>    if a == b {
>    }
> 
>    // Programmer thinks about NaNs, realizes they can simplify their existing 
> code:
>    if a == b {
>    }
> 
> What are the downsides of this?
> 
>    (a) it will confuse sometimes experts who expect IEEE 754 semantics.
>    (b) any code that uses `a != a` as an idiom for detecting NaNs will be 
> broken.
> 
> (b) is by far the bigger risk. It *will* result in some bugs. Hopefully less 
> than result from people failing to consider NaNs. The only real risk with (a) 
> is that we get a biennial rant posted to hacker news about Swift equality 
> being broken, and the response is basically “read the docs, use &== if you 
> want that behavior”.

One more thought — and it’s crazy enough that I’m not even sure it’s worth 
posting — does Swift’s `Equatable` semantics require that `(a == b) != (a != 
b)` always evaluate to `true`? Because it seems like the arguments for having 
`.nan == .nan` return `false` would apply for `!=` as well. Without getting 
into trapping, faulting or returning a `Bool?` / `Maybe` / `Tern`, I can’t 
think of anything else that’d get a developer’s attention faster than the same 
value being both not equal and not not equal to itself.

I mean, is that likely to cause any more bugs than having `.nan == .nan` return 
`true`?

I *think* yes, but I tend to use `.isNaN`, so I’m not sure.

- Dave Sweeris
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Reply via email to