<div dir="ltr">On Thu, Oct 26, 2017 at 4:57 PM, Greg Parker <span dir="ltr">&lt;<a href="mailto:gparker@apple.com" target="_blank">gparker@apple.com</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"><div style="word-wrap:break-word;line-break:after-white-space"><br><div><span class="gmail-"><blockquote type="cite"><div>On Oct 26, 2017, at 11:47 AM, Xiaodi Wu via swift-dev &lt;<a href="mailto:swift-dev@swift.org" target="_blank">swift-dev@swift.org</a>&gt; wrote:</div><br class="gmail-m_-3098831811998198719Apple-interchange-newline"><div><div dir="ltr">On Thu, Oct 26, 2017 at 1:30 PM, Jonathan Hull <span dir="ltr">&lt;<a href="mailto:jhull@gbis.com" target="_blank">jhull@gbis.com</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"><div style="word-wrap:break-word">Now you are just being rude. We all want Swift to be awesome… let’s try to keep things civil.</div></blockquote><div><br></div><div>Sorry if my reply came across that way! That wasn&#39;t at all the intention. I really mean to ask you those questions and am interested in the answers:</div><div><br></div><div>Unless I misunderstand, you&#39;re arguing that your proposal is superior to Rust&#39;s design because of a new operator that returns `Bool?` instead of `Bool`; if so, how is it that you haven&#39;t reproduced Rust&#39;s design problem, only with the additional syntax involved in unwrapping the result?</div><div><br></div><div>And if, as I understand, your argument is that your design is superior to Rust&#39;s *because* it requires unwrapping, then isn&#39;t the extent to which people will avoid using the protocol unintentionally also equally and unavoidably the same extent to which it makes Numeric more cumbersome?</div></div></div></div></div></blockquote><div><blockquote type="cite"><br></blockquote></div><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div></div><div>You said it was impossible, so I gave you a very quick example showing that the current behavior was still possible.  I wasn’t recommending that everyone should only ever use that example for all things.</div><div><br></div><div>For FloatingPoint, ‘(a &amp;== b) == true’ would mimic the current behavior (bugs and all). It may not hold for all types.</div></div></blockquote><div><br></div><div>No, the question was how it would be possible to have these guarantees hold for `Numeric`, not merely for `FloatingPoint`, as the purpose is to use `Numeric` for generic algorithms. This requires additional semantic guarantees on what you propose to call `&amp;==`.</div></div></div></div></div></blockquote><div><br></div></span>Would something like this work?</div><div><br></div><div>Numeric.== -&gt; Bool </div><div>traps on NaN etc.</div></div></blockquote><div><br></div><div>This is unsatisfactory for several reasons:</div><div><br></div><div>- If it is not tolerable for NaN to trap when doing math with floating-point values (and the very notion of &quot;quiet NaN&quot; is predicated on that insight), then it cannot be tolerable for NaN to trap in generic numeric code.<br></div><div><br></div><div>- As the whole raison d&#39;être of `Numeric` is to permit useful generic numeric algorithms, `Numeric.==` must offer the best practicable approximation of mathematical equality for any conforming type. On a concrete numeric type, it would be exceedingly user-hostile if `==` did not represent the best practicable approximation of mathematical equality for that type. Therefore, `Numeric.==` must be the same operator as `FloatingPoint.==` and `Integer.==`. Despite necessary differences between floating-point and integer values, these two concrete operators are spelled the same way because they are both the best practicable approximations of mathematical equality for the numeric values that their respective types attempt to model (see below). If `Numeric.==` does not offer the closest approximations of mathematical equality available for conforming types, there is little point to offering `Numeric` as a generic protocol.</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"><div style="word-wrap:break-word;line-break:after-white-space"><div></div><div><div>Numeric.==? -&gt; Bool? </div><div>returns nil on NaN etc. You likely don&#39;t want this unless you know something about floating-point.</div><div><br></div></div><div>Numeric.&amp;== -&gt; Bool</div><div>is IEEE equality. You should not use this unless you are a floating-point expert.</div></div></blockquote><div><br></div><div>I think we are proceeding from different starting points here.</div><div><br></div><div>It would be contrary to all sense to have a method named `Int.==` be anything other than the best practicable approximation of mathematical equality for `Int`. The same holds for floating-point types.</div><div><br></div><div>Either the IEEE definition of floating-point equality is the best such approximation, or it is not. If it is not, then IEEE equality should not be spelled `==` on any type or in any context. But, having weighed all the alternatives, a committee of floating-point experts has blessed this definition over others. As I understand it, this definition treats the sequence of bits as the real number it attempts to represent to the greatest extent possible, abstracting away encoding and representation issues, and it excludes from the relation all NaNs because they are not in the domain of real numbers.</div><div><br></div><div>So my starting point, then, is that (based on IEEE expertise) there is one and only one proper definition of `==` for floating-point types, and that it is the IEEE definition. You *should* use this definition in all places to test for whether two floating-point values are equal. And Swift *should* present IEEE equality as *the* go-to operator for equivalence of floating-point values (which the core team has already declared on this list to represent the uncountable set of real numbers and not the finite set of representable numbers).</div><div><br></div><div>A proper design for `Equatable` and `Comparable` would accommodate floating-point types while also making it possible to write generic algorithms that behave correctly. It should be a non-goal to make floating-point `==` anything other than what it is (i.e., IEEE-compliant). Nor is it necessary (or, perhaps, even desirable) to eliminate consideration of NaN from generic code. The only goal here (or at least, my only goal here) is to ensure that writing generic code that uses `==` which behaves properly with NaN is no more difficult than writing floating-point-specific code that uses `==` which behaves properly with NaN.</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"><div style="word-wrap:break-word;line-break:after-white-space"><div></div><div>The experts can get high performance or sophisticated numeric behavior. The rest of us who naïvely use == get a relatively foolproof floating-point model. (There is no difference among these three operators for fixed-size integers, of course.)</div><div><br></div><div>This is analogous to what Swift does with integer overflow. I would further argue the other Numeric operators like + should be extended to the same triple of trap or optional or just-do-it. We already have two of those three operators for integer addition after all.</div><div><br></div><div>Numeric.+ -&gt; T</div><div>traps on FP NaN and integer overflow</div></div></blockquote><div><br></div><div>Again, `Numeric.+` is and must be the same as `FloatingPoint.+` and `Integer.+`. They are spelled the same way because they are both the best practicable approximations of mathematical addition for the numeric values that their respective types attempt to model. Wraparound is an inferior approximation of mathematical addition, for example, because its semantics take into consideration the underlying representation of the integral value as a fixed-length series of bits.</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"><div style="word-wrap:break-word;line-break:after-white-space"><div></div><div>Numeric.+? -&gt; T?</div><div>returns nil on FP NaN and integer overflow</div><div><br></div><div>Numeric.&amp;+ -&gt; T</div><div>performs FP IEEE addition and integer wraparound</div></div></blockquote><div><br></div><div>These two operations have entirely distinct semantics. No useful generic algorithm could be written that uses this operator correctly.</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"><div style="word-wrap:break-word;line-break:after-white-space"><div></div><div><br></div><div><span class="gmail-"><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div>The whole point is that you have to put thought into how you want to deal with the optional case where the relation’s guarantees have failed.<div><br></div><div>If you need full performance, then you would have separate overrides on Numeric for members which conform to FloatingPoint (where you could use &amp;==) and Equatable (where you could use ==). As you get more generic, you lose opportunities for optimization. That is just the nature of generic code. The nice thing about Swift is that you have an opportunity to specialize if you want to optimize more. Once things like conditional conformances come online, all of this will be nicer, of course.</div></div></div></blockquote><div><br></div><div>This is a non-starter then. Protocols must enable useful generic code. What you&#39;re basically saying is that you do not intend for it to be possible to use methods on `Numeric` to ask about level 1 equivalence in a way that would not be prohibitively expensive. This, again, eviscerates the purpose of `Numeric`.</div></div></div></div></div></blockquote><div><br></div></span><div>I&#39;m not sure that there is a performance problem. If your compiled code is actually making calls to generic comparison functions then you have already lost the high performance war. Any place where the compiler knows enough to use a specialized comparison function should also be a place where the compiler can optimize away unnecessary floating-point checks.</div><div><br></div><div>Let me make an analogous objection to the current Numerics design. How do you get the highest performance addition operator in a generic context? Currently you can&#39;t, because Numeric.+ checks for integer overflow.</div><span class="gmail-"><div><br></div><br><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>The point I&#39;m making here, again, is that there are legitimate uses for `==` guaranteeing partial equivalence in the generic context. The approximation being put forward over and over is that generic code always requires full equivalence and concrete floating-point code always requires IEEE partial equivalence. That is _not true_. Some generic code (for instance, that which uses `Numeric`) relies on partial equivalence semantics and some floating-point code can nonetheless benefit from a notion of full equivalence.</div></div></div></div></div></blockquote><div><br></div></span><div>I agree that providing a way to get IEEE equality in a generic context is useful. I am not convinced that Numeric.== -&gt; Bool is the right place to provide it.</div></div><span class="gmail-HOEnZb"><font color="#888888"><div><br></div><div><br></div><div>-- </div><div>Greg Parker     <a href="mailto:gparker@apple.com" target="_blank">gparker@apple.com</a>     Runtime Wrangler</div><div><br></div><div><br></div></font></span></div></blockquote></div><br></div></div>