> On Apr 25, 2017, at 8:28 PM, Jonathan Hull via swift-evolution > <[email protected]> wrote: > > >> On Apr 25, 2017, at 7:17 PM, Xiaodi Wu <[email protected] >> <mailto:[email protected]>> wrote: >> >> On Tue, Apr 25, 2017 at 8:38 PM, Jonathan Hull <[email protected] >> <mailto:[email protected]>> wrote: >> >>> On Apr 25, 2017, at 5:25 PM, Xiaodi Wu <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>> On Tue, Apr 25, 2017 at 6:53 PM, Jonathan Hull <[email protected] >>> <mailto:[email protected]>> wrote: >>> I just wanted to ask for more detail on why this is a non-starter (it seems >>> like most of my ideas are dismissed as “non-starters”, but I am rarely >>> given a detailed reason why). >>> >>> Migration would be a renaming from ‘Double' to ‘Double?’, but it wouldn’t >>> be cosmetic. It would free us to use a non-optional Double, where we can >>> guarantee the answer wouldn’t be NaN/nil. We would, as you say, have >>> functions like ‘cos(Double?)->Double?’ which propagate the optional, but we >>> could also add a ‘cos(Double)->Double’ overload which guarantees an actual >>> result. For something like Tan, we would only have the optional version >>> because the answer may actually be undefined, even when the input isn't. >>> >>> Leave aside how one might implement such a system for a moment. The first >>> major issue is that your idea does not address the issues we're talking >>> about here: >>> >>> We are debating, for instance, how to compare arrays with elements of type >>> `Double`. In your design, the question remains how we would compare arrays >>> with elements of type `Double?`. If the answer is that you cannot compare >>> arrays of type `[Double?]`, then we have a problem, because that's the type >>> that people will use when they ingest data that might contain NaN. Sure, >>> they can unwrap each element before using their data, but they can also >>> test each element with `isNaN` today. We are trying to *improve* on the >>> user experience of comparing arrays of floating point values *without* >>> checking if they contain NaN, not trying to take that feature away. >> >> It solves the main problem we are dealing with in Comparable because >> defining Comparable on non-optional Double (without possibility of NaN) is >> fairly simple. >> >> Yes, but `Double` would not the currency type for floating point work. Since >> any input might contain NaN, people will be working pervasively with >> `Double?`. Some consideration as to how the type would have to be designed >> (I will leave that as an exercise to the reader) would reveal that `Double` >> would either have trapping arithmetic operators or no arithmetic operators >> at all. Who would use such a type? And for what purpose? No, you've just >> pushed the problem from Double to Double?, but the issue is unsolved, and >> now we have two types where before we had one. > > Any value of type ‘Double’ would be guaranteed not to contain NaN. Some > things coming in from ObjC land might use ‘Double?', but that is only a > single ‘guard let' statement away from ‘Double’. Once you have a Double, you > would in most cases get a Double back out. In the event that you do receive a > ‘Double?’, you know that you actually do have to deal with the optional case > (either through chaining or bailing early in the case of nil). It enforces > proper behavior which would likely be implemented in a buggy way under the > current system. > > You would probably have trapping arithmetic for divide by zero, just like > Int. The other option is to return an optional, but you and Dave have > previously made strong arguments for trapping. > > >> You could compare an array of ‘Double?' in the same way you could compare an >> array of ‘Int?’ now. It would work the same way optionals work everywhere >> else. >> >> Right, but again, `Double?` would be the currency type for floating point >> work. And you have still not told me how arrays of `Double?` elements would >> work. Would two arrays of NaN compare equal? Would two NaNs compare equal? >> If NaN == NaN, then we've got a problem. The starting point for this >> discussion is that, when working with floating point types, NaN != NaN. It's >> required by IEEE standards, and it's relied upon by users. > > ‘Double?' would only be the currency type for interfacing with external > (non-swift) code. Most swift code would still just use Double… and when it > requires ‘Double?’, you know you need to deal with the possibility of nil. > Right now, NaN is just ignored for the most part… even in some standard > library code. > > >> You would just test for a lack of numerical result by checking the optional >> in the usual Swift way: guard let, if let, etc... >> >> >>> Your design also doesn't address the problem of how NaN should compare with >>> NaN. Only now, you've pushed the problem to `Optional`. By design, every >>> `Optional<T>.none` compares equal to every other `Optional<U>.none` (yes, >>> even of different types). >> >> Yes, the one major difference in behavior, which I mentioned in my earlier >> email, is that we are replacing NaN != NaN with nil == nil. Everything else >> should behave the same. The part that saves us here, is that Swift forces >> you to explicitly consider the optional case. >> >> Basically, I am taking the spirit/intent of the law over the letter of it. >> As far as I can tell, (quiet) NaN was originally designed to provide the >> functionality which we use optional for in Swift. The NaN != NaN thing came >> out of limitations of the computers in the 1980’s because you needed a way >> to tell if something was NaN or not (and isNaN() didn’t exist yet). >> >> I'll refer you to Steve Canon's answer on StackOverflow: >> http://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values/1573715#1573715 >> >> <http://stackoverflow.com/questions/1565164/what-is-the-rationale-for-all-comparisons-returning-false-for-ieee754-nan-values/1573715#1573715> > I get what he is saying about not being able to suddenly change C’s > definition to the opposite boolean value (even if they would have designed it > differently for modern systems). We aren’t C though. And nil is a different > name than NaN. This gives us a way to make a change without breaking > conformance.
I really like this idea, but it’s not true to say this doesn’t break IEEE-compliance if what we expose as NaN no longer compares not equal to itself. > >> Bottom line is, whatever it's designed for, it's here to stay. I'm *not* on >> any IEEE committee; I'm not qualified to design an alternative universe of >> floating point types; and the Swift evolution mailing list (or even Swift >> itself) is not the place to design one. I and others rely on Swift floating >> point types to be IEEE-complaint, so that's where we start the discussion >> here about Comparable. > > It *is* IEEE compliant (or at least the same amount we currently are), what > is different is the way we interface with that in code. You can think of > Swift Double as a simple (runtime-cost-free) wrapper around the compiler’s > double that limits how you can touch it (in order to provide additional > safety guarantees). Sounds very swifty to me… we do similar things with > other C constructs using various wrappers. We aren’t getting rid of NaN > behind the scenes, just packaging it’s use in a way which aligns with Swift > as a whole… What changes is the programer’s mental model. The bits are > exactly the same. > > If the naming is what bothers you, we could even just create a “new” Swift > type that is this wrapper, and then discourage the use of Double outside of > places where NaN is needed. I feel like NaN is actually needed in relatively > few places though… > >> >> The programmer (mental) model would be that Swift Double just doesn’t have >> NaN, and anywhere where you would normally return NaN, you return nil >> instead. >> >> Look, you can already use Double? today. There is no barrier to trying it >> out for yourself. However, `Double?.none` doesn't mean the same thing as >> `Double.nan`. The former indicates that _there is no value_; the latter >> indicates that there _is_ a value, it's just _not a number_. Suppose I parse >> a list of numbers into an array. If I ask for [Double].last and get nil, >> it's telling me that _there are no elements in the array_. If I get .nan, >> it's telling me that there *is* a value, it just wasn't a number. In the >> former case, I'd do nothing. In the latter case, I might prompt the user: >> this isn't a number! >> >> However, the property of using NaN’s bits to represent nil let’s us inter-op >> seamlessly with C and ObjC (or any other language’s) code. They just treat >> it as a double with NaN as normal (including NaN != NaN) and we interface >> with it as ‘Double?' >> >> I'm going to sound like a broken record, now. Whether floating point types >> in Swift conform to IEEE standards is _not_ up for discussion, afaict; >> that's simply a given. Now, around that constraint, we're trying to design a >> revised Comparable protocol. Code written today for floating point work >> expects NaN != NaN. That is just something that is and will forever be. We >> break source compatibility if we change that. > > As I said above, they *do* conform to those standards… they just don’t expose > the full capabilities directly to the end programmer. The capabilities are > still there, you just have to be more explicit about their use. > > I could be wrong, but I believe that in the current version of Swift, the > result of doing comparisons with NaN is actually undefined at the moment… so > it isn’t breaking source compatibility with the defined language. Just with > code which is using implementation artifacts… I think you’re incorrect. It says in the docs for NaN that it always compares false with itself. > > C code might require some simple changes to work in Swift, but as you argued > passionately before, that is already to be expected. We shouldn’t have the > expectation of copy/pasting C code without thinking through the differences > in &+, etc... > >> >>> This did not always work correctly, if I recall, but it does now and it's >>> an intentional part of the design. However, NaN must compare not equal to >>> every NaN. These could not be more different properties. It seems quite >>> absurd on its face that we might want NaN to compare equal to a value of >>> type `Optional<UIViewController>`. >> >> Is there an algorithm that requires NaN != NaN that couldn’t be reasonably >> rewritten to handle nil/optionals instead? >> >> I don't need an algorithm to show you the problem. See this expression: `0 * >> Double.infinity == .infinity * 0` correctly evaluates to false. Zero times >> infinity is simply not equal to infinity times zero. You're suggesting a >> design where the result would be true, and that simply won't fly, because >> it's just not true. > > IEEE 754 says that the result of any comparison with NaN should be > *undefined*. C’s implementation is the one who brings us this idea that NaN > != NaN. The *correct* evaluation according to 754 would be ‘undefined’. We > aren’t breaking 754, just breaking away from a C convention… in a way which > is very much in line with how Swift breaks away from other C conventions. I don’t think this is true. I got excited for a minute and looked it up. Where do you see that comparison behavior is undefined? > > I still haven’t heard any actual working use cases where this property is > actually important other than in legacy C code. > >> >> I haven’t been able to think of one. They are extremely similar, because >> they were designed for the same use-cases. The examples you have given so >> far (e.g. cos(Double)->Double) would all be trivial to migrate. >> >> Thanks, >> Jon >> >> >>> >>> >>> In short, it would actually make people consider conditions which result in >>> NaN because of Swift’s machinery which makes people consider nil. >>> >>> It also allows non-optional Double to easily conform to Comparable, and >>> removes the gotchas around Collections… Pretty big wins for a “cosmetic >>> rename”. The only thing we lose is 'NaN != Nan' (because nil == nil), but >>> then algorithms that had relied on that would be forced to consider the >>> NaN/nil case explicitly because of the optionals. >>> >>> It would also inter-op well with C/ObjC code by just having the compiler >>> overlay Double? for Double… >>> >>> Thanks, >>> Jon >>> >>> >>>> On Apr 16, 2017, at 11:44 AM, Xiaodi Wu <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> >>>> On Sun, Apr 16, 2017 at 1:14 PM, Jonathan Hull <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> >>>>> On Apr 16, 2017, at 10:42 AM, Xiaodi Wu via swift-evolution >>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>> >>>>> The point is that, when you manipulate two real numbers, sometimes there >>>>> is no numeric result. You cannot simply wish this away with a new numeric >>>>> type because it is not an artifact of _modeling_ real numbers but rather >>>>> intrinsic to mathematics itself. >>>> >>>> I agree with the rest of what you said, but I have to disagree on this >>>> point. What I think he is saying is that, in Swift, we really should be >>>> representing the NaN case as an optional instead of a magic value on the >>>> type itself (similar to how swift uses an optional instead of NSNotFound). >>>> >>>> In fact, that might be an actual option here. For ‘Double?’ the compiler >>>> could use the bit pattern for NaN internally to represent .none (I believe >>>> it does similar tricks to save space with other optional types). Then >>>> disallow reference to NaN within swift code. Functions or operations >>>> which could produce NaN would either have to produce an optional or trap >>>> in case of NaN. (e.g. the trig functions would likely return an optional, >>>> and 0/0 would trap). >>>> >>>> I think it would actually lead to much better code because the compiler >>>> would force you to have to explicitly deal with the case of optional/NaN >>>> when it is possible. Migration would be tricky though... >>>> >>>> This is essentially a cosmetic renaming from `Double` to `Double?`. There >>>> are rules for propagating NaN which numeric algorithms expect. For >>>> example, `cos(.nan)` returns a value. If your design is to work, every >>>> function that takes a `Double` will need to take a `Double?`. >>>> >>>> Just as Swift String conforms to Unicode standards, FloatingPoint conforms >>>> to IEEE standards. You'd have to come up with enormous benefits to justify >>>> breaking that. Doing so for Swift 4 is plainly a non-starter. >>>> >>>> Thanks, >>>> Jon > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
