[swift-dev] Rationalizing FloatingPoint conformance to Equatable

Xiaodi Wu xiaodi.wu at gmail.com
Tue Oct 31 21:26:23 CDT 2017


On Tue, Oct 31, 2017 at 5:56 PM, David Sweeris <davesweeris at mac.com> wrote:

>
> On Oct 31, 2017, at 09:07, Stephen Canon via swift-dev <
> swift-dev at 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`?
>

Yes. `!=` is an extension method that cannot be overridden, guaranteed to
return false if `==` returns 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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-dev/attachments/20171031/7066d8af/attachment.html>


More information about the swift-dev mailing list