On Tue, Oct 31, 2017 at 11:07 AM, Stephen Canon <sca...@apple.com> 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 of my premises for this discussion was that concrete NaN != NaN is desirable, correct, and an absolute must-have; the question here was how to write correct *generic* code given that Equatable currently guarantees a == a for all a. Do you disagree with the premise? > One specific response: > > > I see the handling of NaN as a legacy/compatibility issue due to > committee/vendor politics from the 1980’s. I am pretty sure if they could > do it over with modern tech, we would just have isNan() and NaN == NaN… or > we might just have optionals instead. > > With the exception of how they interact with non-floating-point types > (comparisons, conversions to/from integers and strings), NaNs are just > Maybes with fast hardware support. Integers and booleans and strings are > outside the scope of IEEE 754, so it was not in the standard’s purview to > do anything else for those operations. They are not some exotic legacy > thing leftover from the 1980’s; they were quite ahead of their time. > > – Steve
_______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev