On Tue, Apr 25, 2017 at 8:38 PM, Jonathan Hull <[email protected]> wrote:
> > On Apr 25, 2017, at 5:25 PM, Xiaodi Wu <[email protected]> wrote: > > On Tue, Apr 25, 2017 at 6:53 PM, Jonathan Hull <[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. > 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. 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 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. > 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. 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. 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]> wrote: >> >> On Sun, Apr 16, 2017 at 1:14 PM, Jonathan Hull <[email protected]> wrote: >> >>> >>> On Apr 16, 2017, at 10:42 AM, Xiaodi Wu via swift-evolution < >>> [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
