> 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).
- Dave Sweeris
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev