> 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

Reply via email to