> 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