> 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 
> <mailto:daveswee...@mac.com>> wrote:
> 
> On Oct 31, 2017, at 20:58, Xiaodi Wu <xiaodi...@gmail.com 
> <mailto:xiaodi...@gmail.com>> wrote:
> 
>> On Tue, Oct 31, 2017 at 10:50 PM, Xiaodi Wu <xiaodi...@gmail.com 
>> <mailto:xiaodi...@gmail.com>> wrote:
>> On Tue, Oct 31, 2017 at 10:23 PM, David Sweeris <daveswee...@mac.com 
>> <mailto:daveswee...@mac.com>> wrote:
>> 
>>> On Oct 31, 2017, at 7:26 PM, Xiaodi Wu <xiaodi...@gmail.com 
>>> <mailto:xiaodi...@gmail.com>> wrote:
>>> 
>>> On Tue, Oct 31, 2017 at 5:56 PM, David Sweeris <daveswee...@mac.com 
>>> <mailto:daveswee...@mac.com>> wrote:
>>> 
>>> On Oct 31, 2017, at 09:07, Stephen Canon via swift-dev <swift-dev@swift.org 
>>> <mailto: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.

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

Reply via email to