<div dir="ltr">+1 from me. I&#39;ve ended up at a solution like this for my own code. It would be a great to have in the standard lib.</div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Apr 13, 2017 at 4:24 PM, Ben Cohen via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word">Online copy here:<div><br></div><div><a href="https://github.com/airspeedswift/swift-evolution/blob/fa007138a54895e94d22e053122ca24ffa0b2eeb/proposals/NNNN-ComparisonReform.md" target="_blank">https://github.com/<wbr>airspeedswift/swift-evolution/<wbr>blob/<wbr>fa007138a54895e94d22e053122ca2<wbr>4ffa0b2eeb/proposals/NNNN-<wbr>ComparisonReform.md</a></div><div><br></div><div><br><div><blockquote type="cite"><div><div class="h5"><div>On Apr 13, 2017, at 1:17 PM, Ben Cohen via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br class="m_4830314771889068020Apple-interchange-newline"></div></div><div><div><div class="h5"><div style="word-wrap:break-word"><div dir="auto" style="word-wrap:break-word;line-break:after-white-space"><br><div>Hi swift evolution,</div><div><br></div><div>Here’s another pitch, for The Propoosal Formerly Known As Spaceship.</div><div><h1 id="m_4830314771889068020comparison-reform" style="font-size:37px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Comparison Reform</h1><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px">Proposal: <a style="color:rgb(13,110,161);text-decoration:none">SE-NNNN</a></li><li style="font-size:13.19999885559082px">Authors: <a href="https://github.com/codafi" style="color:rgb(13,110,161);text-decoration:none" target="_blank">Robert Widmann</a>, <a href="https://github.com/jadengeller" style="color:rgb(13,110,161);text-decoration:none" target="_blank">Jaden Geller</a>, <a href="https://github.com/harlanhaskins" style="color:rgb(13,110,161);text-decoration:none" target="_blank">Harlan Haskins</a>, <a href="https://github.com/Gankro" style="color:rgb(13,110,161);text-decoration:none" target="_blank">Alexis Beingessner</a>, <a href="https://github.com/airspeedswift" style="color:rgb(13,110,161);text-decoration:none" target="_blank">Ben Cohen</a></li><li style="font-size:13.19999885559082px">Status: <strong style="line-height:1">Awaiting review</strong></li><li style="font-size:13.19999885559082px">Review manager: TBD</li></ul><h2 id="m_4830314771889068020introduction" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Introduction</h2><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">This proposal is for changes that we believe should be made to the existing comparison system by:</p><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px">Making FloatingPoint comparison context sensitive, so that its <code style="line-height:1">Comparable</code> conformance provides a proper total ordering.</li><li style="font-size:13.19999885559082px">Introducing a new ternary-valued <code style="line-height:1">compare(_ other: Self) -&gt; ComparisonResult</code> method.</li><li style="font-size:13.19999885559082px">Removing unnecessary customization points from <code style="line-height:1">Comparable</code>.</li></ul><h2 id="m_4830314771889068020motivation" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Motivation</h2><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The motivation comes from several independent points:</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">1: The standard comparison operators have an intuitive meaning to programmers. Swift encourages encoding that in an implementation of <code style="line-height:1">Comparable</code> that respects the rules of a <a href="https://en.wikipedia.org/wiki/Total_order" style="color:rgb(13,110,161);text-decoration:none" target="_blank">total order</a>. The standard library takes advantage of these rules to provide consistent implementations for sorting and searching generic collections of <code style="line-height:1">Comparable</code> types.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Not all types behave so well in this framework, unfortunately. There are cases where the semantics of a total order cannot apply while still maintaining the traditional definition of “comparison” for these types. Take, for example, sorting an array of <code style="line-height:1">Float</code>s. Today, <code style="line-height:1">Float</code>’s instance of <code style="line-height:1">Comparable</code> follows IEEE-754 and returns <code style="line-height:1">false</code> for all comparisons of <code style="line-height:1">NaN</code>. In order to sort this array, <code style="line-height:1">NaN</code> s are considered outside the domain of <code style="line-height:1">&lt;</code>, and the order of a sorted array containing them is unspecified. Similarly, a Dictionary keyed off floats can leak entries and memory.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">2: Generic algorithms in the Swift Standard Library that make use of the current <code style="line-height:1">Comparable</code> protocol may have to make extra comparisons to determine the ordering of values when <code style="line-height:1">&lt;</code>, <code style="line-height:1">==</code>, and <code style="line-height:1">&gt;</code> should have different behaviours. Having a central operation to return complete ordering information should provide a speedup for these operations.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">3: The existing comparison operators don’t “generalize” well. There’s no clean way to add a third or fourth argument to <code style="line-height:1">&lt;</code> to ask for non-default semantics. An example where this would be desirable would be specifying the locale or case-sensitivity when comparing Strings.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">4: <code style="line-height:1">Comparable</code> is over-engineered in the customization points it provides: to our knowledge, there’s no good reason to ever override <code style="line-height:1">&gt;=</code>, <code style="line-height:1">&gt;</code>, or <code style="line-height:1">&lt;=</code>. Each customization point bloats vtables and mandates additional dynamic dispatch.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">5: When quickly writing a <code style="line-height:1">Comparable</code> type, it is easier to implement a single ternary statement than to separately implement <code style="line-height:1">==</code> and <code style="line-height:1">&lt;</code>.</p><h2 id="m_4830314771889068020proposed-solution" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Proposed solution</h2><h3 id="m_4830314771889068020L-code-comparisonresult--code-" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif"><code style="line-height:1">ComparisonResult</code></h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Foundation’s ComparisonResult type will be mapped into Swift as</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:92px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto"><span class="m_4830314771889068020hljs-meta" style="color:rgb(153,153,153);font-weight:bold">@objc</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-class"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">enum</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span> : <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">Int</span> </span>{
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span> orderedAscending = -<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">1</span>
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span> orderedSame = <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span> orderedDescending = <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">1</span>
}</code></pre><h3 id="m_4830314771889068020L-code-comparable--code-" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif"><code style="line-height:1">Comparable</code></h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Comparable will be changed to have a new ternary comparison method: <code style="line-height:1">compare(_ other: Self) -&gt; ComparisonResult</code>. <code style="line-height:1">x.compare(y)</code><wbr> specifies where to place x relative to y. So if it yields <code style="line-height:1">.orderedAscending</code>, then x comes before y. This will be considered the new “main” dispatch point of Comparable that implementors should provide.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Most code will continue to use <code style="line-height:1">&lt;</code> or <code style="line-height:1">==</code>, as it will be optimal for their purposes. However code that needs to make a three-way branch on comparison can use the potentially more efficient <code style="line-height:1">compare</code>. Note that <code style="line-height:1">compare</code> is only expected to be more efficient in this specific case. If a two-way branch is all that’s being done, <code style="line-height:1">&lt;</code> will be more efficient in many cases (if only because it’s easier for the optimizer).</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">For backwards compatibility reasons, <code style="line-height:1">compare</code> will have a default implementation defined in terms of <code style="line-height:1">&lt;</code>, but to enable only using <code style="line-height:1">compare</code>, <code style="line-height:1">&lt;</code> and <code style="line-height:1">==</code> will also have default implementations in terms of <code style="line-height:1">compare</code>.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The compiler will verify that either <code style="line-height:1">compare</code>, or <code style="line-height:1">&lt;</code> and <code style="line-height:1">==</code>, are provided by every type that claims to conform to <code style="line-height:1">Comparable</code>. This will be done in some unspecified way unavailable outside the standard library (it can be made available to in the future, but that’s an unnecessary distraction for this proposal).</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Types that wish to provide comparison “variants” can do so naturally by adding <code style="line-height:1">compare</code> methods with additional arguments. e.g. <code style="line-height:1">String.compare(_ other: Self, in: Locale) -&gt; ComparisonResult</code>. These have no language-level connection to <code style="line-height:1">Comparable</code>, but are still syntactically connected, implying the same total order semantics. This makes them easier to discover, learn, and migrate to.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">To reduce bloat, the operators <code style="line-height:1">&lt;=</code>, <code style="line-height:1">&gt;=</code>, and <code style="line-height:1">&gt;</code> will be removed from the set of requirements that the <code style="line-height:1">Comparable</code> protocol declares. These operators will however continue to exist with the current default implementations.</p><h3 id="m_4830314771889068020L-code-floatingpoint--code-" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif"><code style="line-height:1">FloatingPoint</code></h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">No changes will be made to the <code style="line-height:1">FloatingPoint</code> protocol itself. Instead, new extensions will be added to it to change the behaviour of comparison.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The new behaviour centers around the fact that <code style="line-height:1">compare(_: Self) -&gt; ComparisonResult</code> will provide a total ordering that’s consistent with Level 2 in the IEEE 754 (2008) spec. This is mostly the same as the standard (Level 1) IEEE ordering, except:</p><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px"><code style="line-height:1">-0 &lt; +0</code></li><li style="font-size:13.19999885559082px"><code style="line-height:1">NaN == NaN</code></li><li style="font-size:13.19999885559082px"><code style="line-height:1">NaN &gt; +Inf</code> (an arbitrary choice, NaN can be placed anywhere in the number line)</li></ul><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Level 2’s distinguishing of <code style="line-height:1">-0</code> and <code style="line-height:1">+0</code> is a bit strange, but is consistent with Equatable’s Substitutability requirement. <code style="line-height:1">-0</code> and <code style="line-height:1">+0</code> have different behaviours: <code style="line-height:1">1/-0 = -Inf</code> while <code style="line-height:1">1/+0 = +Inf</code>. The main problem this can lead to is that a keyed collection may have two “0” entries. In practice this probably won’t be a problem because it’s fairly difficult for the same algorithm to produce both <code style="line-height:1">-0</code> and <code style="line-height:1">+0</code>. Any algorithm that does is also probably concerned with the fact that <code style="line-height:1">1.0E-128</code> and <code style="line-height:1">2.0E-128</code> are considered distinct values.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Note: IEEE specifies several other potential total orderings: level 3, level 4, and the totalOrder predicate. For our purposes, these orderings are too aggressive in distinguishing values that are semantically equivalent in Swift. For most cases, the relevant issue is that they distinguish different encodings of NaN. For more exotic encodings that don’t guarantee normalization, these predicates also consider <code style="line-height:1">10.0e0 &lt; 1.0e1</code> to be true. An example where this can occur is <em style="line-height:1">IEEE-754 decimal coded floating point</em>, which FloatingPoint is intended to support.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">We will then make the comparison operators (<code style="line-height:1">&lt;</code>, <code style="line-height:1">&lt;=</code>, <code style="line-height:1">==</code>, <code style="line-height:1">!=</code>, <code style="line-height:1">&gt;=</code>, <code style="line-height:1">&gt;</code>) dispatch to one of <code style="line-height:1">compare(_:)</code> or FloatingPoint’s IEEE comparison methods (<code style="line-height:1">isLess</code>, <code style="line-height:1">isEqual</code>, <code style="line-height:1">isLessThanOr<wbr>EqualTo</code>) based on the context.</p><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px">If the context knows the type is FloatingPoint, then level 1 ordering will be used.</li><li style="font-size:13.19999885559082px">If the context only knows the type is Comparable or Equatable, then level 2 ordering will be used.</li></ul><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">This results in code that is explicitly designed to work with FloatingPoint types getting the expected IEEE behaviour, while code that is only designed to work with Comparable types (e.g. <code style="line-height:1">sort</code> and <code style="line-height:1">Dictionary</code>) gets more reasonable total ordering behaviour.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">To clarify: <code style="line-height:1">Dictionary</code> and <code style="line-height:1">sort</code> <wbr>won’t somehow detect that they’re being used with <code style="line-height:1">FloatingPoint</code> types and use level 1 comparisons. Instead they will unconditional use level 2 behaviour. For example:</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:220px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">let</span> nan = <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0.0</span>/<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0.0</span>

<span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">printEqual</span>&lt;T: Equatable&gt;<span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> x: T, <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> y: T)</span></span> {
  <span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">print</span>(x == y)
}

<span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">printEqualFloats</span>&lt;T: FloatingPoint&gt;<span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> x: T, <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> y: T)</span></span> {
  <span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">print</span>(x == y)
}

<span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">print</span>(nan == nan)          <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// false, (concrete)</span>
printEqual(nan, nan)       <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// true,  (generic Equatable but not FloatingPoint)</span>
printEqualFloats(nan, nan) <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// false, (generic FloatingPoint)</span></code></pre><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">If one wishes to have a method that works with all Equatable/Comparable types, but uses level 1 semantics for FloatingPoint types, then they can simply provide two identical implementations that differ only in the bounds:</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:204px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">let</span> nan = <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0.0</span>/<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0.0</span>

<span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">printEqual</span>&lt;T: Equatable&gt;<span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> x: T, <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> y: T)</span></span> {
  <span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">print</span>(x == y)
}

<span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">printEqual</span>&lt;T: FloatingPoint&gt;<span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> x: T, <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> y: T)</span></span> {
  <span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">print</span>(x == y)
}

printEqual(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>, <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>)           <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// true (integers use `&lt;T: Equatable&gt;` overload)</span>
printEqual(nan, nan)       <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// false (floats use `&lt;T: FloatingPoint&gt;` overload)</span></code></pre><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">As a result of this change, hashing of floats must be updated to make all NaNs hash equally. -0 and +0 will also no longer be expected to hash equally. (Although they might as an implementation detail – equal values must hash the same, unequal values <em style="line-height:1">may</em> hash the same.)</p><h3 id="m_4830314771889068020misc-standard-library" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Misc Standard Library</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Types that conform to <code style="line-height:1">Comparable</code> should be audited for places where implementing or using <code style="line-height:1">Comparable</code> would be a win. This update can be done incrementally, as the only potential impact should be performance. As an example, a default implementation of <code style="line-height:1">compare(_:)</code> for Array will likely be suboptimal, performing two linear scans to determine the result in the worst-case. (See the default implementation provided in the detailed design.)</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Some free functions will have <code style="line-height:1">&lt;T: FloatingPoint&gt;</code> overloads to better align with IEEE-754 semantics. This will be addressed in a follow-up proposal. (example: <code style="line-height:1">min</code> and <code style="line-height:1">max</code>)</p><h2 id="m_4830314771889068020detailed-design" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Detailed Design</h2><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The protocols will be changed as follows:</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em"><code style="line-height:1">ComparisonResult</code>, currently a type found in Foundation, will be sunk into the Swift Standard Library:</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:1548px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto"><span class="m_4830314771889068020hljs-meta" style="color:rgb(153,153,153);font-weight:bold">@objc</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-class"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">enum</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span>: <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">Int</span>, <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">Equatable</span> </span>{
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span> orderedAscending = -<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">1</span>
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span> orderedSame = <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span> orderedDescending = <span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">1</span>
}

<span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-class"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">protocol</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">Comparable</span>: <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">Equatable</span> </span>{
  <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">compare</span><span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> other: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">Self</span>)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span>

  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> &lt; (lhs: Self, rhs: Self) -&gt; <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">Bool</span>
}

<span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">extension</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">Comparable</span> </span>{
  <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">compare</span><span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> other: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">Self</span>)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span> {
    <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">self</span> == other {
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedSame
    } <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">else</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">self</span> &lt; other {
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedAscending
    } <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">else</span> {
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedDescending
    }
  }
}

<span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> &lt; &lt;T: Comparable&gt;<span class="m_4830314771889068020hljs-params">(lhs: T, rhs: T)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Bool</span> {
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> lhs.compare(rhs) == .orderedAscending
}

<span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// IEEE comparison operators (these implementations already exist in std)</span>
<span class="m_4830314771889068020hljs-class"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">extension</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">FloatingPoint</span> </span>{
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> == <span class="m_4830314771889068020hljs-params">(lhs: T, rhs: T)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Bool</span> {
    <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> lhs.isEqual(to: rhs)
  }

  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> &lt; (lhs: T, rhs: T) -&gt; <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">Bool</span> </span>{
    <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> lhs.isLess(than: rhs)
  }

  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> &lt;= (lhs: T, rhs: T) -&gt; <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">Bool</span> </span>{
    <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> lhs.isLessThanOrEqualTo(rhs)
  }

  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> &gt; <span class="m_4830314771889068020hljs-params">(lhs: T, rhs: T)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Bool</span> {
    <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> rhs.isLess(than: lhs)
  }

  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> &gt;= <span class="m_4830314771889068020hljs-params">(lhs: T, rhs: T)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Bool</span> {
    <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> rhs.isLessThanOrEqualTo(lhs)
  }
}


<span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// Comparable comparison operators (provides a total ordering)</span>
<span class="m_4830314771889068020hljs-class"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">extension</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(68,85,136);font-weight:bold">FloatingPoint</span> </span>{
  @_inline
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">compare</span><span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> other: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">Self</span>)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span> {
    <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// Can potentially be implemented more efficiently -- this is just the clearest version</span>
    <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">self</span>.isLess(than: other) {
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedAscending
    } <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">else</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> other.isLess(than: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">self</span>) {
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedDescending
    } <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">else</span> {
      <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// Special cases</span>

      <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// -0 &lt; +0</span>
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">self</span>.isZero &amp;&amp; other.isZero {
        <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// .plus == 0 and .minus == 1, so flip ordering to get - &lt; +</span>
        <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> (other.sign <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">as</span> <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Int</span>).compare(<span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">self</span>.sign <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">as</span> <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Int</span>)
      }

      <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// NaN == NaN, NaN &gt; +Inf</span>
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">self</span>.isNaN {
        <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> other.isNaN {
          <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedSame
        } <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">else</span> {
          <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedDescending
        }
      } <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">else</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">if</span> other.isNaN {
        <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedAscending
      } 

      <span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// Otherwise equality agrees with normal IEEE</span>
      <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">return</span> .orderedSame
    }
  }

  @_implements(<span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Equatable</span>.==)
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">_comparableEqual</span><span class="m_4830314771889068020hljs-params">(lhs: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">Self</span>, rhs: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">Self</span>)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Bool</span> {
    lhs.compare(rhs) == .orderedSame
  }

  @_implements(<span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Comparable</span>.&lt;)
  <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">public</span> <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">static</span> <span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">_comparableLessThan</span><span class="m_4830314771889068020hljs-params">(lhs: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">Self</span>, rhs: <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">Self</span>)</span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">Bool</span> {
    lhs.compare(rhs) == .orderedDescending
  }
}</code></pre><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Note that this design mandates changes to the compiler:</p><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px"><div style="word-wrap:break-word;margin:0px;line-height:1.3125em">@_implements (or an equivalent mechanism) must be implemented to get the context-sensitive FloatingPoint behaviour.</div></li><li style="font-size:13.19999885559082px"><div style="word-wrap:break-word;margin:0px;line-height:1.3125em">The compiler must verify that either == and &lt;, or compare(_:) is overridden by every type that conforms to Comparable.</div></li></ul><h2 id="m_4830314771889068020source-compatibility" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Source compatibility</h2><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Users of <code style="line-height:1">ComparisonResult</code> will be able to use it as normal once it becomes a standard library type.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Existing implementors of <code style="line-height:1">Comparable</code> will be unaffected, though they should consider implementing the new <code style="line-height:1">compare</code> method as the default implementation may be suboptimal.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Consumers of <code style="line-height:1">Comparable</code> will be unaffected, though they should consider calling the <code style="line-height:1">compare</code> method if it offers a performance advantage for their particular algorithm.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Existing implementors of <code style="line-height:1">FloatingPoint</code> should be unaffected – they will automatically get the new behaviour as long as they aren’t manually implementing the requirements of Equatable/Comparable.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Existing code that works with floats may break if it’s relying on some code bounded on <code style="line-height:1">&lt;T: Equatable/Comparable&gt;</code>providing IEEE semantics. For most algorithms, NaNs would essentially lead to unspecified behaviour, so the primary concern is whether -0.0 == +0.0 matters.</p><h2 id="m_4830314771889068020abi-stability" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">ABI stability</h2><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">This must be implemented before ABI stability is declared.</p><h2 id="m_4830314771889068020effect-on-api-resilience" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Effect on API resilience</h2><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">N/A</p><h2 id="m_4830314771889068020alternatives-considered" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Alternatives Considered</h2><h3 id="m_4830314771889068020spaceship" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Spaceship</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Early versions of this proposal aimed to instead provide a <code style="line-height:1">&lt;=&gt;</code> operator in place of <code style="line-height:1">compare</code>. The only reason we moved away from this was that it didn’t solve the problem that comparison didn’t generalize.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Spaceship as an operator has a two concrete benefits over <code style="line-height:1">compare</code> today:</p><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px">It can be passed to a higher-order function</li><li style="font-size:13.19999885559082px">Tuples can implement it</li></ul><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">In our opinion, these aren’t serious problems, especially in the long term.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Passing <code style="line-height:1">&lt;=&gt;</code> as a higher order function basically allows types that aren’t Comparable, but do provide <code style="line-height:1">&lt;=&gt;</code>, to be very ergonomically handled by algorithms which take an optional ordering function. Types which provide the comparable operators but don’t conform to Comparable are only pervasive due to the absence of conditional conformance. We shouldn’t be designing our APIs around the assumption that conditional conformance doesn’t exist.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">When conditional conformance is implemented, the only should-be-comparable-but-aren’<wbr>t types that will remain are tuples, which we should potentially have the compiler synthesize conformances for.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Similarly, it should one day be possible to extend tuples, although this is a more “far future” matter. Until then, the <code style="line-height:1">(T, T) -&gt; Bool</code> predicate will always also be available, and <code style="line-height:1">&lt;</code> can be used there with the only downside being a potential performance hit.</p><h3 id="m_4830314771889068020just-leave-floats-alone" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Just Leave Floats Alone</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The fact that sorting floats leads to a mess, and storing floats can lead to memory leaks and data loss isn’t acceptable.</p><h3 id="m_4830314771889068020just-make-floats-only-have-a-total-order" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Just Make Floats Only Have A Total Order</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">This was deemed too surprising for anyone familiar with floats from any other language. It would also probably break a lot more code than this change will.</p><h3 id="m_4830314771889068020just-make-floats-not-comparable" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Just Make Floats Not Comparable</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Although floats are more subtle than integers, having places where integers work but floats don’t is a poor state of affairs. One should be able to sort an array of floats and use floats as keys in data structures, even if the latter is difficult to do correctly.</p><h3 id="m_4830314771889068020partialcomparable" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">PartialComparable</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">PartialComparable would essentially just be Comparable without any stated ordering requirements, that Comparable extends to provide ordering requirements. This would be a protocol that standard IEEE comparison could satisfy, but in the absence of total ordering requirements, PartialComparable is effectively useless. Either everyone would consume PartialComparable (to accept floats) or Comparable (to have reasonable behaviour).</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The Rust community adopted this strategy to little benefit. The Rust libs team has frequently considered removing the distinction, but hasn’t because doing it backwards compatibly would be complicated. Also because merging the two would just lead to the problems Swift has today.</p><h3 id="m_4830314771889068020different-names-for--code-compare--code--and--code-comparisonresult--code-" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Different Names For <code style="line-height:1">compare</code> and <code style="line-height:1">ComparisonResu<wbr>lt</code></h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">A few different variants for <code style="line-height:1">ComparisonResult</code> and its variants were considered:</p><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px">Dropping the <code style="line-height:1">ordered</code> part of <code style="line-height:1">ComparisonResult</code>’s cases e.g. <code style="line-height:1">.ascending</code></li><li style="font-size:13.19999885559082px">Naming of <code style="line-height:1">ComparisonResult</code> as <code style="line-height:1">SortOrd<wbr>er</code></li><li style="font-size:13.19999885559082px"><code style="line-height:1">enum Ordering { case less, equal, greater }</code> (<a href="https://doc.rust-lang.org/std/cmp/enum.Ordering.html" style="color:rgb(13,110,161);text-decoration:none" target="_blank">as used by Rust</a>)</li><li style="font-size:13.19999885559082px">Case values of <code style="line-height:1">inOrder</code>, <code style="line-height:1">same</code>, <code style="line-height:1">outOfOrder</code></li></ul><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The choice of case names is non-trivial because the enum shows up in different contexts where different names makes more sense. Effectively, one needs to keep in mind that the “default” sort order is ascending to map between the concept of “before” and “less”.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The before/after naming to provide the most intuitive model for custom sorts – referring to <code style="line-height:1">ascending</code> or <code style="line-height:1">less</code> is confusing when trying to implement a descending ordering. Similarly the inOrder/outOfOrder naming was too indirect – it’s more natural to just say where to put the element. If the enum should focus on the sorting case, calling it <code style="line-height:1">SortOrder</code> would help to emphasize this fact.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">This proposal elects to leave the existing Foundation name in-place. The primary motivation for this is that use of the <code style="line-height:1">compare</code> function will be relatively rare. It is expected that in most cases users will continue to make use of <code style="line-height:1">==</code> or <code style="line-height:1">&lt;</code>, returning boolean values (the main exception to this will be in use of the parameterized <code style="line-height:1">String</code> <wbr>comparisons). As such, the source compatibility consequences of introducing naming changes to an existing type seems of insufficient benefit.</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">The method <code style="line-height:1">compare(_:)</code> does not fully comport with the API naming guidelines. However, it is firmly established with current usage in Objective-C APIs, will be fairly rarely seen/used (users will usually prefer <code style="line-height:1">&lt;</code>, <code style="line-height:1">==</code> etc), and alternatives considered, for example <code style="line-height:1">compared(to:)</code>, were not a significant improvement.</p><h3 id="m_4830314771889068020add-overloads-for--code--t--t----gt--comparisonresult--code-" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Add Overloads for <code style="line-height:1">(T, T) -&gt; ComparisonResult</code></h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">It would be slightly more ergonomic to work with ComparisonResult if existing methods that took an ordering predicate also had an overload for <code style="line-height:1">(T, T) -&gt; ComparisonResult</code>. As it stands, a case-insensitive sort must be written as follows:</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:28px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto">myStrings.<span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">sort</span> { $<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>.compare(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> other: $<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">1</span>, <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span>: .insensitive) == .orderedAscending }</code></pre><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">With the overload, one could write:</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:28px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto">myStrings.<span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">sort</span> { $<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>.compare($<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">1</span>, <span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">case</span>: .insensitive) }</code></pre><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">we decided against providing these overloads because:</p><ul style="margin-top:21px;margin-bottom:21px;padding-left:1.5em;color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;font-size:11.999999046325684px"><li style="font-size:13.19999885559082px">The existing algorithms in the standard library can’t benefit from them (only binary comparisons).</li><li style="font-size:13.19999885559082px">They bloat up the standard library (and any library which intends to match our API guidelines).</li><li style="font-size:13.19999885559082px">They potentially introduce confusion over “which” comparison overload to use.</li></ul><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">And because we can change our mind later without concern for source or ABI stability, as these overloads would be additive.</p><h2 id="m_4830314771889068020future-work" style="color:rgb(17,17,17);font-size:27px;line-height:42px;margin-top:42px;margin-bottom:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Future Work</h2><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">This section covers some topics which were briefly considered, but were identified as reasonable and possible to defer to future releases. Specifically they should be backwards compatible to introduce even after ABI stability. Two paths that are worth exploring:</p><h3 id="m_4830314771889068020ergonomic-generalized-comparison-for-keyed-containers" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">Ergonomic Generalized Comparison for Keyed Containers</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">Can we make it ergonomic to use an (arbitrary) alternative comparison strategy for a Dictionary or a BinaryTree? Should they be type-level Comparators, or should those types always store a <code style="line-height:1">(Key, Key) -&gt; ComparisonResult</code> closure?</p><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">We can avoid answering this question because <code style="line-height:1">Dictionary</code> is expected to keep a relatively opaque (resilient) ABI for the foreseeable future, as many interesting optimizations will change its internal layout. Although if the answer is type-level, then Default Generic Parameters must be accepted to proceed down this path.</p><h3 id="m_4830314771889068020comparisonresult-conveniences" style="color:rgb(17,17,17);margin:21px 0px;font-size:20px;line-height:21px;font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif">ComparisonResult Conveniences</h3><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">There are a few conveniences we could consider providing to make ComparisonResult more ergonomic to manipulate. Such as:</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:140px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto"><span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// A way to combine orderings</span>
<span class="m_4830314771889068020hljs-function"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">func</span> <span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">ComparisonResult</span>.<span class="m_4830314771889068020hljs-title" style="color:rgb(153,0,0);font-weight:bold">breakingTiesW<wbr>ith</span><span class="m_4830314771889068020hljs-params">(<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">_</span> order: <span class="m_4830314771889068020hljs-params">()</span></span></span> -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span>) -&gt; <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span>

array.<span class="m_4830314771889068020hljs-built_in" style="color:rgb(0,134,179)">sort</span> {
  $<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>.x.compare($<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>.y)
  .breakingTiesWith { $<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">0</span>.y.compare($<span class="m_4830314771889068020hljs-number" style="color:rgb(0,128,128)">1</span>.y) }
  == .orderedAscending 
}</code></pre><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">and</p><pre style="margin-top:21px;margin-bottom:21px;color:rgb(17,17,17);font-size:11.999999046325684px;background-color:rgb(248,248,248);height:76px"><code class="m_4830314771889068020swift m_4830314771889068020hljs" style="line-height:inherit;display:block;overflow-x:auto;padding:0.5em;color:rgb(51,51,51);height:auto"><span class="m_4830314771889068020hljs-keyword" style="font-weight:bold">var</span> inverted: <span class="m_4830314771889068020hljs-type" style="color:rgb(68,85,136);font-weight:bold">ComparisonResult</span>

<span class="m_4830314771889068020hljs-comment" style="color:rgb(153,153,136);font-style:italic">// A perhaps more &quot;clear&quot; way to express reversing order than `y.compared(to: x)`</span>
x.compare(y).inverted</code></pre><p style="color:rgb(17,17,17);font-family:&#39;Helvetica Neue&#39;,Helvetica,Arial,Verdana,sans-serif;word-wrap:break-word;margin:1.3125em 0px;font-size:1.1429em;line-height:1.3125em">But these can all be added later once everyone has had a chance to use them.</p></div></div></div></div></div>______________________________<wbr>_________________<br>swift-evolution mailing list<br><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br></div></blockquote></div><br></div></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote></div><br></div>