<div dir="ltr">Sorry to overwhelm with more emails. I&#39;d like to show some work and further analysis for your consideration that refines the sketch I just wrote:<div><br></div><div>Two FP values a and b can be, with respect to each other:</div><div><br></div><div>* ordered or unordered (per IEEE, NaN compares unordered to everything, including itself)</div><div>* identical or not identical (for these purposes, we adopt Steve&#39;s proposed test for identity: substitutable for all operations; thus +0 is not identical to -0, but different binary representations of the same value are identical)</div><div>* equal or not equal (i.e. the behavior of the == operator today)</div><div><br></div><div>So, if a and b are, with respect to each other:</div><div><br></div><div>* ordered, identical, equal -- this is what happens ordinarily with two equal, non-NaN values</div><div>* ordered, identical, not equal -- this can never happen</div><div>* ordered, not identical, equal -- only +0 and -0</div><div>* ordered, not identical, not equal -- this is what happens ordinarily with two unequal, non-NaN values</div><div><br></div><div>* unordered, identical, equal -- this can never happen, but if NaNs are to be well-behaved (for a true equivalence relation), then we will need an equivalence relation in which NaN == NaN</div><div>* unordered, identical, not equal -- this is what always happens, but if NaNs are to be well-behaved, then such behavior will need to change</div><div>* unordered, not identical, equal -- this can never happen</div><div>* unordered, not identical, not equal -- this is what ordinarily happens with one NaN and one non-NaN value</div><div><br></div><div>Equatable can have === and my proposed ==? as part of its protocol; a generic ==, as originally proposed, would be defined outside the protocol.</div><div>A default implementation of ==? will forward to ===, and the generic == will be defined as `{ return (lhs ==? rhs) ?? (lhs === rhs) }`.</div><div>For floating point, ==? will be specialized and cease to forward to === so that +0 and -0 compare true and NaN and anything compare nil, and floating point == will be defined notionally as `{ return (lhs ==? rhs) ?? false }`.</div><div><br><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Jul 23, 2016 at 3:09 PM, Xiaodi Wu <span dir="ltr">&lt;<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Throwing out some more radical ideas here. Suppose we had instead (taking inspiration from IEEE notation):<div><br></div><div>[Pardon any errors; I&#39;m writing freehand in Gmail]<br><div><br></div><div>infix operator ==? { /* figure out precedence later */ }</div><div><br></div><div>protocol Equatable {</div><div>  static func ==? (lhs: Self, rhs: Self) -&gt; Bool?</div><div>  /* semantics:</div><div>     this function returns nil if lhs and rhs are unordered with respect to each other</div><div>     otherwise, evaluate by means of a legal equivalence relation */</div><div>}</div><div><br></div><div>func == &lt;T: Equatable&gt;(lhs: T, rhs: T) -&gt; Bool {</div><div>  return (lhs ==? rhs) ?? false</div><span class=""><div>}</div><div><br></div><div>protocol Comparable : Equatable {</div><div>  static func &lt;=&gt; (lhs: Self, rhs: Self) -&gt; Ordering</div></span><div>  /* semantics:</div><div>     this is a total ordering; thus:</div><div>     if `(a ==? b) == true`, then `(a &lt;=&gt; b) == .same`</div><div>     if `(a ==? b) == false`, then `(a &lt;=&gt; b) != .same`</div><div>     but, if `(a ==? b) == nil`, then `a &lt;=&gt; b` may yield any result</div><div>  */</div><div><div class="h5"><div>}</div><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Sat, Jul 23, 2016 at 2:35 PM, Pyry Jahkola <span dir="ltr">&lt;<a href="mailto:pyry.jahkola@iki.fi" target="_blank">pyry.jahkola@iki.fi</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Given all this, I propose a simpler model that makes `a &lt;=&gt; b` follow the expected behaviour of &lt; and ==, with the tradeoff that `a &lt;=&gt; .nan` and `.nan &lt;=&gt; b` will abort with a precondition failure:<br>
<br>
1) We keep the current Interface of Equatable  unchanged, with != defined in terms of ==.<br>
<br>
2) Comparable would still refine Equatable, and include all the comparison operators, adding the new &lt;=&gt;:<br>
<br>
    protocol Comparable : Equatable {<br>
      static func &lt;=&gt;(lhs: Self, rhs: Self) -&gt; Ordering<br>
<span>      static func &lt;(lhs: Self, rhs: Self) -&gt; Bool<br>
      static func &gt;(lhs: Self, rhs: Self) -&gt; Bool<br>
</span><span>      static func &lt;=(lhs: Self, rhs: Self) -&gt; Bool<br>
      static func &gt;=(lhs: Self, rhs: Self) -&gt; Bool<br>
    }<br>
<br>
</span>The comparison operators are kept in the interface so that partially ordered types such as Double can be supported in generic code. However, the documentation should recommend against defining `&lt;` manually.<br>
<br>
3) Default implementations for &lt;Self : Comparable&gt; are provided for the following operators: ==, &lt;, &gt;, &lt;=, and &gt;=.<br>
<br>
4) User-defined types will need to define just &lt;=&gt; to conform to Comparable. (Even == can be omitted!)<br>
<br>
5) FloatingPoint types implement custom versions of ==, &lt;, &gt;, &lt;=, and &gt;= using the standard IEEE 754 definition (i.e. comparisons involving NaN return false). Zero is zero; `0.0 == -0.0 &amp;&amp; !(-0.0 &lt; 0.0)` holds.<br>
<br>
6) FloatingPoint types implement &lt;=&gt; as:<br>
<br>
    func &lt;=&gt; &lt;T : FloatingPoint&gt;(lhs: T, rhs: T) -&gt; Ordering {<br>
      if lhs &lt; rhs { return .ascending }<br>
      if rhs &lt; lhs { return .descending }<br>
      precondition(lhs == rhs)<br>
      return .same<br>
    }<br>
<br>
7) Algorithms using &lt;=&gt; directly should mention so in their documentation as a precondition that they require total order between elements. Many generic algorithms can be defined in terms of == or &lt;, and should.<br>
<br>
If we took the oroginally planned route that distinguished between identities such as -0.0 vs. +0.0, or between the 2⁴⁹ - 2 ≈ 5.6 × 10¹⁴ possible NaN values that Double has, we&#39;d also need to consider other oddballs like the difference and ordering between the Strings &quot;ä&quot; and &quot;a\u{308}&quot;, which are considered equal but produce a different Unicode representation. I think it&#39;s best to hide those identities behind another interface than Equatable and Comparable, and let the protocols serve more mundane application logic.<br>
<span><font color="#888888"><br>
— Pyry<br>
</font></span><div><div><br>
&gt; Dave Abrahams wrote:<br>
&gt;<br>
&gt;<br>
&gt;&gt; on Sat Jul 23 2016, Xiaodi Wu &lt;<a href="http://xiaodi.wu-AT-gmail.com" rel="noreferrer" target="_blank">xiaodi.wu-AT-gmail.com</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; On Fri, Jul 22, 2016 at 11:34 PM, Stephen Canon &lt;<a href="mailto:scanon@apple.com" target="_blank">scanon@apple.com</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt;&gt;&gt; The point of this design is that `===` means identity and that `.same `<br>
&gt;&gt;&gt;&gt; also means identity.<br>
&gt;&gt;&gt;&gt;<br>
&gt;&gt;&gt;&gt; Since this is new territory I suppose we get to decide what identity<br>
&gt;&gt;&gt;&gt; means for floating point.  Should +0 and -0 have the same identity or<br>
&gt;&gt;&gt;&gt; not?  I’ll leave the answer to folks more knowledgable about numerics<br>
&gt;&gt;&gt;&gt; than I.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Boy, I take my wife out for a movie and come back to 50 new messages on SE.<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; I need to read the entire thread more carefully, but off the top of my<br>
&gt;&gt;&gt; head, I think that `-0 === +0` is False.  If we’re going to have an<br>
&gt;&gt;&gt; `isSame` / `isIdentical` / whatever it&#39;s called, I would expect it to imply<br>
&gt;&gt;&gt; substitutability.  Although -0 == +0, they are not equivalent when<br>
&gt;&gt;&gt; substituted:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; - 1/(-0) != 1/0<br>
&gt;&gt;&gt; - Float(-0).sign != Float(+0).sign<br>
&gt;&gt;&gt; - etc<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; This probably then implies that `&lt;=&gt;` is not `.same` either.  I’ll read<br>
&gt;&gt;&gt; the rest of this and respond more completely tomorrow.<br>
&gt;&gt;<br>
&gt;&gt; Eagerly await your evaluation of the discussion. In the meantime:<br>
&gt;&gt;<br>
&gt;&gt; I think Dave&#39;s view that `===` defines identity in terms of &quot;essential&quot;<br>
&gt;&gt; qualities implies that two identical values can be<br>
&gt;&gt; different/non-substitutable in &quot;inessential&quot; qualities. For generic<br>
&gt;&gt; purposes, the sign of zero could be one such inessential quality.<br>
&gt;<br>
&gt; Yes, and I think our view of how people work with numbers in swift (and<br>
&gt; their protocol conformances) reflect this approach.<br>
&gt;<br>
&gt; <a href="http://article.gmane.org/gmane.comp.lang.swift.evolution/16321" rel="noreferrer" target="_blank">http://article.gmane.org/gmane.comp.lang.swift.evolution/16321</a><br>
&gt;<br>
&gt; My sense is that we want to choose the default notions of identity and<br>
&gt; ordering so as to support the way people think about these numeric<br>
&gt; types, inexact though it may be.  Therefore, finding 0.0 in a sequence<br>
&gt; of floats should succeed when the sequence contains -0.0, and a stable<br>
&gt; sort on floating point keys should preserve the relative order of all<br>
&gt; elements having +0.0 and -0.0 keys.<br>
&gt;<br>
&gt; People that want to work with inessential qualities such as the sign of<br>
&gt; zero can always pass Float.totalOrdering (or whatever) to their<br>
&gt; closure-accepting algorithms.<br>
&gt;<br>
&gt; [In order to support the user model, we still need to fix the semantics<br>
&gt; of the default identity and ordering operations so that things like<br>
&gt; sorting and searching work, which is why == and &lt; won&#39;t cut it for these<br>
&gt; purposes]<br>
&gt;<br>
&gt;&gt; On the other hand, the stdlib stride algorithm is going to be borked if -0<br>
&gt;&gt; &lt; +0. Of course, as we already started to do there, we could specialize for<br>
&gt;&gt; floating point and then adjust accordingly. However, it seems to me that<br>
&gt;&gt; every generic algorithm that performs comparisons and can take floating<br>
&gt;&gt; point arguments would have to be specialized to account for floating point<br>
&gt;&gt; -0 != +0 (`index(of:)` being the previous example). This appears to defeat<br>
&gt;&gt; the aim of trying to accommodate FP at all in this revised design for<br>
&gt;&gt; Comparables.<br>
&gt;<br>
&gt; Yes, that would be a disaster, generically speaking.<br>
&gt;<br>
&gt;&gt; The argument for `-0 === +0` is that -0 and +0 should be equivalent when<br>
&gt;&gt; substituted for every comparison operation. For FP operations, you&#39;d<br>
&gt;&gt; continue to test (as you have to test now) `a == b &amp;&amp; a.sign == b.sign` if<br>
&gt;&gt; you cared about the sign of zero. For non-FP arithmetic operations, hmm,<br>
&gt;&gt; not sure how to square that circle.<br>
&gt;<br>
&gt; I followed all of this... except, what are you getting at with that last<br>
&gt; sentence?<br>
&gt;<br>
&gt; --<br>
&gt; Dave<br>
</div></div><div><div>&gt; _______________________________________________<br>
&gt; swift-evolution mailing list<br>
&gt; <a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
&gt; <a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</div></div></blockquote></div><br></div></div></div></div></div>
</blockquote></div><br></div></div></div>