<div dir="ltr">On Sat, Apr 15, 2017 at 10:32 PM, Dave Abrahams 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><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"><span class="gmail-"><br>
on Sat Apr 15 2017, Xiaodi Wu &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br>
<br>
&gt; On Sat, Apr 15, 2017 at 3:12 PM, Dave Abrahams via swift-evolution &lt;<br>
&gt; <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br>
&gt;<br>
&gt;&gt;<br>
&gt;&gt; on Thu Apr 13 2017, Xiaodi Wu &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; &gt; Getting this sorted out is definitely a worthwhile effort. I do have<br>
&gt;&gt; &gt; thoughts about this proposal:<br>
&gt;&gt; &gt;<br>
&gt;&gt; &gt; I continue to have reservations about an identical spelling (e.g. `==`)<br>
&gt;&gt; &gt; giving two different answers with the same values of the same type,<br>
&gt;&gt; &gt; depending on the generic context. It is a very *clever* design, but it is<br>
&gt;&gt; &gt; also a very *subtle* behavior that I can see leading to much confusion<br>
&gt;&gt; and<br>
&gt;&gt; &gt; befuddlement for any user who is not well versed *both* in the<br>
&gt;&gt; intricacies<br>
&gt;&gt; &gt; of IEEE floating point *and* in the intricacies of Swift.<br>
&gt;&gt;<br>
&gt;&gt; I can&#39;t help but think that the concern over confusion here is not<br>
&gt;&gt; informed by any realistic situations.  Please describe<br>
&gt;&gt;<br>
&gt;<br>
&gt; To be clear, I&#39;m not claiming that my concerns about the proposal outweigh<br>
&gt; my enthusiasm for it.<br>
&gt;<br>
&gt; But here, the confusion I&#39;m concerned about stems from the essential<br>
&gt; conclusion by the proposal authors that types (including, but not<br>
&gt; necessarily only limited to FP types) which are ordinarily compared in a<br>
&gt; way that treats certain &quot;special values&quot; differently must also present an<br>
&gt; alternative notion of comparison that accounts for all possible<br>
&gt; values.<br>
<br>
</span>That may be a conclusion, but it&#39;s not an assumption.  For example, it&#39;s<br>
totally reasonable that there is a value of Int (i.e. 0) for which the<br>
requirements of division don&#39;t hold.  We say that 0 is outside the<br>
domain of / when used as a divisor, and we tried to get away with saying<br>
that NaN was outside the domain of ==.  However, it&#39;s also reasonable to<br>
trap on integer division by zero.<br>
<br>
What we have is a situation where values that “misbehave” when given<br>
IEEE semantics occur in normal code and are expected to interoperate<br>
with other floating point values under normal circumstances (such as<br>
when sorting), and not only interoperate but give reasonable results.<br>
<br>
Now, having thought about this a bit more myself, here is a real case<br>
where confusion might occur:<br>
<br>
  if values.contains(.NaN) {<br>
    print(values.filter { $0 != .NaN }) // Surprise, NaN is printed!<br>
  }<br>
<br>
I find this result truly loathsome, but it seems to me that the only<br>
reasonable cure is giving == equivalence relation semantics under all<br>
circumstances.<br>
<span class="gmail-"><br>
&gt; The special casing of certain values already makes comparison<br>
&gt; operations difficult to understand; I guess I&#39;m simply stating what is<br>
&gt; unavoidably true, that having a &quot;non-special-cased&quot; comparison *in<br>
&gt; addition* to that adds additional difficulty.<br>
<br>
</span>Yup.<br>
<span class="gmail-"><br>
&gt; One example where this comes to mind is this: `XCTAssertEqual(+0.0, -0.0)`<br>
&gt; will pass or fail depending on whether XCTAssertEqual provides a<br>
&gt; FP-specific specialization. This is something that cannot be determined by<br>
&gt; reading the testing code itself: that is, it requires not merely knowledge<br>
&gt; about whether or not the call site &quot;knows&quot; that it&#39;s operating on a FP<br>
&gt; type, but also knowledge about whether XCTest itself is FP-aware.<br>
<br>
</span>Yep.<br>
<span class="gmail-"><br>
&gt; The issue increases in difficulty when it comes to non-stdlib types.<br>
&gt; Suppose I were to write a Rational type. (I am writing a Rational type, but<br>
&gt; we can talk about that later.) This Rational type is capable of<br>
&gt; representing NaN (`(0 / 0 as Rational&lt;Int&gt;).isNaN == true`) and, for<br>
&gt; consistency with FP types, `Rational&lt;Int&gt;.nan != Rational&lt;Int&gt;.nan`.<br>
<br>
</span>Hey, it&#39;s your funeral! ;-)<br>
<span class="gmail-"><br>
&gt; If this proposal is implemented, *I* would be responsible for making<br>
&gt; XCTest Rational-aware, vending `XCTAssertEqual&lt;Rational&lt;T&gt;&gt;(_<wbr>:_:)`.<br>
<br>
</span>Now you&#39;re mixing apples and oranges with NaN and +/-0, the latter of<br>
which doesn&#39;t apply to rationals.  The way you make XCTest aware of NaN<br>
is to do the same thing that floating point did: vend the static version<br>
of == separately from the dynamic version, using @_implements (and get<br>
us to drop the underscore).<br>
<br>
XCTestAssertEqual is a perfect example of a generic function: it is (or<br>
should be!) concerned with verifying that results match expectations,<br>
not that “==” behaves according to the specific quirks of a type.<br>
Anything else would make<br>
<br>
   XCTestAssertEqual(<wbr>resultOfComputation, NaN)<br>
<br>
meaningless.</blockquote><div><br></div><div>Hmm, I will admit I have not tried it because I have always assumed that the assertion above is meaningless, and that it ought to be. `XCTAssertTrue(resultOfComputation.isNaN)` is the way I&#39;ve been testing my NaN&#39;s.</div><div><br></div><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"><span class="gmail-">
&gt; In fact, to ensure that users of Rational get the expected behavior<br>
&gt; everywhere, I would have to vend special Rational-aware versions of<br>
&gt; every stdlib, core library, and _third-party_ function that is<br>
&gt; FP-aware, which it is not possible to do.<br>
<br>
</span>I&#39;m sorry, I don&#39;t follow.  Give me an example, please, and also clearly<br>
define “FP-awareness.”<br></blockquote><div><br></div><div>By FP awareness, I mean that in comparing two FP values a function uses `FloatingPoint.==` as opposed to `Comparable.==`. For example, I expect `XCTAssertEqual&lt;T : FloatingPoint&gt;(_:_:)` to be vended as part of XCTest, in order to make sure that `XCTAssertEqual(resultOfComputation, Double.nan)` always fails.</div><div><br></div><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"><span class="gmail-">
&gt;&gt; Actually, the fact that this behavior cannot even be achieved without<br>
&gt;&gt; &gt; currently non-existent compiler features means that it is not possible<br>
&gt;&gt; &gt; to understand what&#39;s truly going on without reading *this document*,<br>
&gt;&gt;<br>
&gt;&gt; This doesn&#39;t seem like a reasonable argument.  New compiler features get<br>
&gt;&gt; documented outside of the proposals they come from.  Nobody&#39;s going to<br>
&gt;&gt; have to go read a proposal to understand what @implements means.<br>
&gt;<br>
&gt; I do not mean that I seriously believe @_implements will never be<br>
&gt; documented. (Although, as an underscored attribute, it does not need to be<br>
&gt; documented outside of the apple/swift repo, in the same way that<br>
&gt; @_transparent and its ilk are not documented outside the repo.)<br>
<br>
</span>Either way, we either end up documenting @_implements or special magic<br>
behavior for floating point types.  Somebody already needs to document<br>
the latter for floating point types (.NaN != .NaN), frankly, even though IEEE is a<br>
commonly implemented standard.<br>
<span class="gmail-"><br>
&gt; I am simply saying that fully understanding how `Comparable` and<br>
&gt; `FloatingPoint` interact with each other will require learning one<br>
&gt; additional advanced feature of the language than is required now,<br>
<br>
</span>Agreed, it will require learning something new.<br>
<span class="gmail-"><br>
&gt; and that this feature does not even currently exist.<br>
<br>
</span>I don&#39;t understand the relevance of that last point though.</blockquote><div><br></div><div>Not critical to the argument. Just that it will impact all users of Swift. It&#39;s not as though some particularly studious user of Swift 3 will be rewarded with a head start because he or she has devoted time to careful study of the language.</div><div> </div><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"><span class="gmail-"><br>
&gt;<br>
&gt;<br>
&gt;&gt; &gt; after mastering *both* IEEE floating point *and* Swift<br>
&gt;&gt; &gt; generics/protocols/extensions/<wbr>static vs. dynamic dispatch. All to use<br>
&gt;&gt; &gt; `==` correctly.<br>
&gt;&gt;<br>
&gt;&gt; I don&#39;t understand this argument.  The *whole* point of this proposal is<br>
&gt;&gt; that you use `==` correctly *without* the need for any special knowledge.<br>
&gt;&gt;<br>
&gt;&gt; &gt; Which is to say, most people will simply not even know if they happen<br>
&gt;&gt; &gt; to be using the `==` they did not intend to use.<br>
&gt;&gt;<br>
&gt;&gt; Most people aren&#39;t aware that IEEE comparison is quirky and don&#39;t know<br>
&gt;&gt; what they intend with respect to those semantics, but anyone who *is*<br>
&gt;&gt; aware of his intention has an easy way to understand what&#39;s happening.<br>
&gt;&gt; Does this code know it&#39;s operating on floating point numbers?  If so,<br>
&gt;&gt; it&#39;s IEEE.  If not, it&#39;s an equivalence relation.<br>
&gt;&gt;<br>
&gt;&gt; &gt; I think consideration should be given to a design that achieves a<br>
&gt;&gt; &gt; user-facing but not onerous differentiation between level 1 and level 2<br>
&gt;&gt; &gt; equality. However, the only one I can think of is essentially a different<br>
&gt;&gt; &gt; shade of the `PartiallyComparable` alternative already outlined in the<br>
&gt;&gt; &gt; document.<br>
&gt;&gt;<br>
&gt;&gt; I am *deeply* opposed to such a protocol.  It is purely syntactic in<br>
&gt;&gt; nature and thus useless for correctly constraining generic algorithms.<br>
&gt;<br>
&gt; But there do exist types for which some pairs of values cannot be compared<br>
&gt; to each other. A generic algorithm that blows up if a value cannot be<br>
&gt; compared to another value should not operate on such types, no?<br>
<br>
</span>I don&#39;t know what you mean by “blows up.” If you mean, “traps,” I think<br>
that&#39;s fine.  If you mean, “misbehaves,” I think that&#39;s really hard to<br>
justify unless it&#39;s easy for users to keep these values from ever<br>
entering their program.  The algorithm itself doesn&#39;t know all the types<br>
on which it can operate, so its documentation isn&#39;t in a position to<br>
warn users about this precondition.  Therefore, the caller, who got the<br>
values from somewhere and may be generic itself, is in no position to<br>
check for the problem.  The only cure is to bottleneck production of<br>
values of that type and ensure that those values never get beyond some<br>
boundary in the code.  I don&#39;t think that&#39;s practical in the case of<br>
floating point, not least because ordinary operations on legit values<br>
can produce the problematic values.<br>
<span class="gmail-"><br>
&gt;&gt; People will use it anyway, resulting in algorithms that statically<br>
&gt;&gt; accept, but are incorrect for, floating point.  In my opinion there&#39;s<br>
&gt;&gt; only one feasible answer that doesn&#39;t use the static/dynamic distinction<br>
&gt;&gt; we&#39;ve proposed: throw IEEE semantics under the bus, making it available<br>
&gt;&gt; only under a different syntax.<br>
&gt;&gt;<br>
&gt;&gt; This would be a drastic move whose primary downside is that floating<br>
&gt;&gt; point code ported from C would need to be carefully audited and may<br>
&gt;&gt; become less easy to read.  But at least it would be viable.<br>
&gt;&gt;<br>
&gt;<br>
&gt; Yes I agree that it would be a much more drastic move that would make<br>
&gt; Swift difficult to use for numerics, and I, at least, would not want<br>
&gt; to go down that road.<br>
<br>
</span>I&#39;d rather not, myself.  The choices are not good:<br>
<br>
1. Complexity and occasional surprises that can be documented and<br>
   explained.<br>
2. Silent misbehavior.<br>
3. Separate syntax for distinct semantics.<br>
<br>
#2 is the only one I find completely unacceptable.<br>
<div><div class="gmail-h5"><br>
&gt;&gt; &gt; Yet I cannot help but think that the rejected alternative may be<br>
&gt;&gt; &gt; advantageous in one key aspect. `FloatingPoint` comparison is in a<br>
&gt;&gt; &gt; sense &quot;less refined&quot; (not exactly precise language, I know) than the<br>
&gt;&gt; &gt; level 2 ordering proposed here, at least in the sense that the latter<br>
&gt;&gt; &gt; offers more semantic guarantees about the relationships between<br>
&gt;&gt; &gt; comparison operators.<br>
&gt;&gt;<br>
&gt;&gt; No, they are effectively refinement siblings.  If it was a hierarchical<br>
&gt;&gt; relationship, we could just make the floating point types conform to<br>
&gt;&gt; Equatable.  But floating point types are *required* by IEEE to have<br>
&gt;&gt; comparison semantics that conflict with Equatable.<br>
&gt;&gt;<br>
&gt;&gt; &gt; It&#39;s weird that the less refined `FloatingPoint` refines the more<br>
&gt;&gt; &gt; refined `Comparable`,<br>
&gt;&gt;<br>
&gt;&gt; Do you want to be able to sort floating point numbers without providing<br>
&gt;&gt; a comparison predicate (one that has to be spelled less obviously than<br>
&gt;&gt; &quot;&lt;&quot;)?  If so, floating point numbers must be Comparable.  If not, we<br>
&gt;&gt; could talk about breaking this refinement relationship.<br>
&gt;&gt;<br>
&gt;&gt; &gt; and I think the acrobatics with compiler support illustrate how the<br>
&gt;&gt; &gt; design is actively working against Swift&#39;s overarching direction.<br>
&gt;&gt;<br>
&gt;&gt; It&#39;s not more acrobatic than things we already do in the standard<br>
&gt;&gt; library to ensure that people transparently see the right behavior (see<br>
&gt;&gt; .lazy), and we could probably even find ways to do it without language<br>
&gt;&gt; features.  It would be less principled, harder to understand, and more<br>
&gt;&gt; fragile than designing a language feature that addresses the need<br>
&gt;&gt; directly.<br>
&gt;<br>
&gt; I have an incipient idea. It begins with:<br>
&gt;<br>
&gt; enum ComparisonResult {<br>
&gt;   case orderedAscending, orderedSame, orderedDescending, unordered<br>
&gt; }<br>
&gt;<br>
&gt; I am sure there is something I am missing which makes this design a<br>
&gt; horrible idea, but I&#39;m interested in hearing them.<br>
<br>
</div></div>It&#39;s not a horrible idea, but to be fair it&#39;s not really a design yet,<br>
either.  You haven&#39;t said anything about what it&#39;s supposed to mean, how<br>
it is supposed to be used, how people write, use, and compose generic<br>
algorithms with it, how it deals with floating point, etc.<br></blockquote><div><br></div><div>I&#39;ve evolved my thinking based on the discussion. Let&#39;s see:</div><div><br></div><div>```</div><div>public enum ComparisonResult : Equatable {</div><div>  case orderedAscending, equivalent, orderedDescending, unordered</div><div>  // I have renamed one case, in the hopes of emphasizing that two values</div><div>  // that compare `equivalent` are not merely ordered the same, but should</div><div>  // satisfy the three conditions of an equivalence relation.</div><div>}</div><div>// I will have to leave the question of how to bridge from Obj-C<br></div><div>// NSComparisonResult up to more capable hands.</div><div><br></div><div>public protocol Comparable : Equatable {</div><div>  func compared(to other: Self) -&gt; ComparisonResult</div><div>}</div><div>// This will have to be modified as necessarily to support compiler magic</div><div>// necessary for source-compatibility with Swift 3</div><div><br></div><div>extension Comparable {</div><div>  public static func == (lhs: Self, rhs: Self) -&gt; Bool {</div><div>    let comparison = lhs.compared(to: rhs)</div><div><div>    precondition(comparison != .unordered)</div><div>    return comparison == .equivalent</div></div><div>  }</div><div><br></div><div>  public static func &lt; (lhs: Self, rhs: Self) -&gt; Bool {</div><div>    let comparison = lhs.compared(to: rhs)</div><div>    precondition(comparison != .unordered)</div><div>    return comparison == .orderedAscending</div><div>  }</div><div>  // etc.</div><div><br></div><div>  // Something I thought I&#39;d never want to see, but on reflection not terrible:</div><div>  public static func &amp;== (lhs: Self, rhs: Self) -&gt; Bool {</div><div>    return lhs.compared(to: rhs) == .equivalent</div><div>  }</div><div><br></div><div>  public static func &amp;&lt; (lhs: Self, rhs: Self) -&gt; Bool {</div><div>    return lhs.compared(to: rhs) == .orderedAscending</div><div>  }</div><div>  // etc.</div><div>}</div><div><br></div><div>extension FloatingPoint : Comparable {</div><div>  public func compared(to other: Self) -&gt; ComparisonResult {</div><div>    if isNaN || other.isNaN { return .unordered }<br></div><div>    if isLess(than: other) { return .orderedAscending }</div><div>    if other.isLess(than: self) { return .orderedDescending }</div><div><br></div><div><div>    // On reconsideration, probably actually least surprising if +0.0 == -0.0</div><div>    // no matter what. It does not matter that division can give different</div><div>    // results for two equivalent values, only that the three rules of an</div><div>    // equivalence relation will hold, and it does.</div><div>    //</div><div>    // If a user is really savvy to the sign of zero, there is FloatingPoint.sign,</div><div>    // which is necessary in any case for working with FP operations that</div><div>    // distinguish between +0 and -0. I can&#39;t think of a generic algorithm over</div><div>    // all Comparable types that could make use of the distinction between +0</div><div>    // and -0.</div></div><div>    return .equivalent</div><div>  }</div><div>}</div><div>```</div><div><br></div><div>In this design, `==`, `&lt;`, etc. correspond to IEEE &quot;signaling&quot; operators, while `&amp;==`, `&amp;&lt;`, etc. correspond to C-style operators, which IEEE calls &quot;quiet&quot; and actually notates using &quot;?==&quot;, &quot;?&lt;&quot;, etc.</div><div><br></div></div></div></div>