On Wed, Nov 1, 2017 at 13:47 David Sweeris <daveswee...@mac.com> wrote:
> > On Nov 1, 2017, at 10:21 AM, Xiaodi Wu <xiaodi...@gmail.com> wrote: > > > On Tue, Oct 31, 2017 at 23:43 David Sweeris <daveswee...@mac.com> wrote: > >> >> On Oct 31, 2017, at 20:58, Xiaodi Wu <xiaodi...@gmail.com> wrote: >> >> On Tue, Oct 31, 2017 at 10:50 PM, Xiaodi Wu <xiaodi...@gmail.com> wrote: >> >>> On Tue, Oct 31, 2017 at 10:23 PM, David Sweeris <daveswee...@mac.com> >>> wrote: >>> >>>> >>>> On Oct 31, 2017, at 7:26 PM, Xiaodi Wu <xiaodi...@gmail.com> wrote: >>>> >>>> On Tue, Oct 31, 2017 at 5:56 PM, David Sweeris <daveswee...@mac.com> >>>> wrote: >>>> >>>>> >>>>> 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`? >>>>> >>>> >>>> Yes. `!=` is an extension method that cannot be overridden >>>> >>>> >>>> Wait, what? So if I have a `Password` type, and want to trigger extra >>>> logging if the `!=` function is called too many times within a second or >>>> something, that won't get called in generic code? That seems... >>>> unintuitive... >>>> >>> >>> That's correct, as it is for all protocol extension methods (for >>> example, most of the collection APIs). >>> >> >> Incidentally, even if it were desirable to log on comparison, why would >> you want to log only if `!=` returns `true` and not when `==` returns >> `false`? >> >> >> Mostly because if I ever wrote a Password class, I’d probably make it >> handle all the hashing and stuff internally so that the correct usage would >> look like `if enteredPW != storedPW {...}`. I know they’re (generally) the >> same, but I tend to think `_ != _` rather than `!(_ == _)`. >> >> I know it’d be a different thread (on a different mailing list), but does >> anyone care if I propose that we change this? I’m strongly in favor of >> providing a default implementation of !=, but I can’t think of why it >> shouldn’t just be a *default* method... with the developers still having >> the option to provide their own if they think they can get there faster >> than the default implementation (or maybe even just because they want to >> set a breakpoint on `!=` but not `==` or something). >> > > You’d have to provide a convincing use case, but I’m not aware of one > where evaluating “if x != y” should ever be different from “if !(x == y)”. > Certainly, if you want != to log something, you would want == to log > something. It is a feature, not a bug, that Swift guarantees that these are > synonyms. > > > Yes, but I might not want them to log the *same* thing... in particular > (in this example), I'd want "==" to show up in one, and "!=" in the other. > Mostly I'm just really surprised that we're swapping functions around such > that any side effects might be different for generic vs concrete code. I > guess another way to handle it would be to issue a warning when a type that > conforms to a protocol implements a non-overrideable function of that > protocol. > This has been raised in various forms over multiple years; any changes to this area are very tricky to square with retroactive conformance.
_______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev