<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Nov 1, 2017, at 10:21 AM, Xiaodi Wu <<a href="mailto:xiaodi.wu@gmail.com" class="">xiaodi.wu@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="auto" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class="Apple-interchange-newline">On Tue, Oct 31, 2017 at 23:43 David Sweeris <<a href="mailto:davesweeris@mac.com" class="">davesweeris@mac.com</a>> wrote:<br class=""></div><blockquote class="gmail_quote" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><br class=""><div class="">On Oct 31, 2017, at 20:58, Xiaodi Wu <<a href="mailto:xiaodi.wu@gmail.com" target="_blank" class="">xiaodi.wu@gmail.com</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div class="">On Tue, Oct 31, 2017 at 10:50 PM, Xiaodi Wu<span class="Apple-converted-space"> </span><span class=""><<a href="mailto:xiaodi.wu@gmail.com" target="_blank" class="">xiaodi.wu@gmail.com</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div class=""><div class="gmail_extra"><div class="gmail_quote"><div class=""><div class="m_-7145618473697793909h5">On Tue, Oct 31, 2017 at 10:23 PM, David Sweeris<span class="Apple-converted-space"> </span><span class=""><<a href="mailto:davesweeris@mac.com" target="_blank" class="">davesweeris@mac.com</a>></span><span class="Apple-converted-space"> </span>wrote:<br class=""><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div style="word-wrap: break-word;" class=""><div class=""><div class="m_-7145618473697793909m_-4560982478301145588gmail-h5"><br class=""><div class=""><blockquote type="cite" class=""><div class="">On Oct 31, 2017, at 7:26 PM, Xiaodi Wu <<a href="mailto:xiaodi.wu@gmail.com" target="_blank" class="">xiaodi.wu@gmail.com</a>> wrote:</div><br class="m_-7145618473697793909m_-4560982478301145588gmail-m_5435354574384829892Apple-interchange-newline"><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px;" class="">On Tue, Oct 31, 2017 at 5:56 PM, David Sweeris<span class="m_-7145618473697793909m_-4560982478301145588gmail-m_5435354574384829892Apple-converted-space"> </span><span class=""><<a href="mailto:davesweeris@mac.com" target="_blank" class="">davesweeris@mac.com</a>></span><span class="m_-7145618473697793909m_-4560982478301145588gmail-m_5435354574384829892Apple-converted-space"> </span>wrote:<br class=""><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin: 0px 0px 0px 0.8ex; border-left-width: 1px; border-left-style: solid; border-left-color: rgb(204, 204, 204); padding-left: 1ex;"><div dir="auto" class=""><div class=""><div class="m_-7145618473697793909m_-4560982478301145588gmail-m_5435354574384829892m_5991692719474945925h5"><br class=""><div class="">On Oct 31, 2017, at 09:07, Stephen Canon via swift-dev <<a href="mailto:swift-dev@swift.org" target="_blank" class="">swift-dev@swift.org</a>> wrote:<br class=""><br class=""></div><blockquote type="cite" class=""><div class=""><span class="">[Replying to the thread as a whole]</span><br class=""><span class=""></span><br class=""><span class="">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.</span><br class=""><span class=""></span><br class=""><span class="">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.</span><br class=""><span class=""></span><br class=""><span class="">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:</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer fails to consider NaN behavior.</span><br class=""><span class=""> if a == b {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class="">but the correct implementation would be:</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer has thought about how to handle NaN here.</span><br class=""><span class=""> if a == b || (a.isNaN && b.isNaN) {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class="">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:</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer fails to consider NaN behavior. This now traps if a or b is NaN.</span><br class=""><span class=""> // That’s somewhat safer, but almost surely not the desired behavior.</span><br class=""><span class=""> if a == b {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer considers NaNs. They now cannot use `==` until they rule out</span><br class=""><span class=""> // either a or b is NaN. This actually makes the code *more* complicated and</span><br class=""><span class=""> // less readable. Alternatively, they use `&==` or whatever we call the unsafe</span><br class=""><span class=""> // comparison and it’s just like what we had before, except now they have a</span><br class=""><span class=""> // “weird operator”.</span><br class=""><span class=""> if (!a.isNaN && !b.isNaN && a == b) || (a.isNaN && b.isNaN) {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class="">Now what happens if we return Bool?</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer fails to consider NaN behavior. Maybe the error when they</span><br class=""><span class=""> // wrote a == b clues them in that they should. Otherwise they just throw in</span><br class=""><span class=""> // a `!` and move on. They have the same bug they had before.</span><br class=""><span class=""> if (a == b)! {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer considers NaNs. Unchanged from what we have currently,</span><br class=""><span class=""> // except that we replace || with ??.</span><br class=""><span class=""> if a == b ?? (a.isNaN && b.isNaN) {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class="">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:</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer fails to consider NaN behavior. Now their code works!</span><br class=""><span class=""> if a == b {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class=""> // Programmer thinks about NaNs, realizes they can simplify their existing code:</span><br class=""><span class=""> if a == b {</span><br class=""><span class=""> }</span><br class=""><span class=""></span><br class=""><span class="">What are the downsides of this?</span><br class=""><span class=""></span><br class=""><span class=""> (a) it will confuse sometimes experts who expect IEEE 754 semantics.</span><br class=""><span class=""> (b) any code that uses `a != a` as an idiom for detecting NaNs will be broken.</span><br class=""><span class=""></span><br class=""><span class="">(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”.</span><br class=""></div></blockquote><br class=""></div></div><div class="">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)`<span class="m_-7145618473697793909m_-4560982478301145588gmail-m_5435354574384829892Apple-converted-space"> </span><i class="">always</i><span class="m_-7145618473697793909m_-4560982478301145588gmail-m_5435354574384829892Apple-converted-space"> </span>evaluate to `true`?</div></div></blockquote><div class=""><br class=""></div><div class="">Yes. `!=` is an extension method that cannot be overridden</div></div></div></div></div></blockquote><br class=""></div></div></div><div class="">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...</div></div></blockquote><div class=""><br class=""></div></div></div><div class="">That's correct, as it is for all protocol extension methods (for example, most of the collection APIs).</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">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`?</div></div></div></div></div></blockquote><br class=""></div><div dir="auto" class=""><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">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 `!(_ == _)`.</span><br class=""></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class=""><br class=""></span></div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">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<span class="Apple-converted-space"> </span><i class="">default</i><span class="Apple-converted-space"> </span>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).</span></div></div></blockquote><div dir="auto" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><div dir="auto" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">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.</div></div></blockquote><br class=""></div><div>Yes, but I might not want them to log the <i class="">same</i> 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.</div><div><br class=""></div><div>- Dave Sweeris</div></body></html>