> On Oct 31, 2017, at 9:07 AM, 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)! { > }
Same bug, yes, but at least it'd crash when the nil gets force-unwrapped. > // Programmer considers NaNs. Unchanged from what we have currently, > // except that we replace || with ??. > if a == b ?? (a.isNaN && b.isNaN) { > } `(a == b) != false` would be more compact (assuming they didn't want to use `&==` for some reason), but, yes, that's still not as simple as `a == b` > 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”. "<whatever> != <whatever>" is a pretty specific pattern... Could we warn or have a fix-it or something on that? > 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. Agreed. I wish int/string/etc supported that bit of semantics... it'd make dealing with floats more consistent with other types. - Dave Sweeris _______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev