On Sat, Apr 22, 2017 at 5:51 PM, Dave Abrahams <dabrah...@apple.com> wrote:
> > on Sat Apr 22 2017, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote: > > > On Sat, Apr 22, 2017 at 4:14 PM, Dave Abrahams <dabrah...@apple.com> > wrote: > > > >> > >> on Tue Apr 18 2017, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote: > >> > >> > On Tue, Apr 18, 2017 at 10:40 AM, Ben Cohen via swift-evolution < > >> > swift-evolution@swift.org> wrote: > >> > > >> >> > >> >> On Apr 17, 2017, at 9:40 PM, Chris Lattner via swift-evolution < > >> >> swift-evolution@swift.org> wrote: > >> >> > >> >> > >> >> On Apr 17, 2017, at 9:07 AM, Joe Groff via swift-evolution < > >> >> swift-evolution@swift.org> wrote: > >> >> > >> >> > >> >> On Apr 15, 2017, at 9:49 PM, Xiaodi Wu via swift-evolution < > >> >> swift-evolution@swift.org> wrote: > >> >> > >> >> For example, I expect `XCTAssertEqual<T : FloatingPoint>(_:_:)` to be > >> >> vended as part of XCTest, in order to make sure that `XCTAssertEqual( > >> resultOfComputation, > >> >> Double.nan)` always fails. > >> >> > >> >> > >> >> Unit tests strike me as an example of where you really *don't* want > >> level > >> >> 1 comparison semantics. If I'm testing the output of an FP > operation, I > >> >> want to be able to test that it produces nan when I expect it to, or > >> that > >> >> it produces the right zero. > >> >> > >> >> > >> >> I find it very concerning that == will have different results based > on > >> >> concrete vs generic type parameters. This can only lead to > significant > >> >> confusion down the road. I’m highly concerned about situations where > >> >> taking a concrete algorithm and generalizing it (with generics) will > >> change > >> >> its behavior. > >> >> > >> >> > >> >> It is already the case that you can start with a concrete algorithm, > >> >> generalize it, and get confusing results – just with a different > >> starting > >> >> point. If you start with a concrete algorithm on Int, then generalize > >> it to > >> >> all Equatable types, then your algorithm will have unexpected > behavior > >> for > >> >> floats, because these standard library types fail to follow the rules > >> >> explicitly laid out for conforming to Equatable. > >> >> > >> >> This is bad. Developers need to be able to rely on those rules. The > >> >> standard library certainly does: > >> >> > >> >> let a: [Double] = [(0/0)] > >> >> var b = a > >> >> > >> >> // true, because fast path buffer pointer comparison: > >> >> a == b > >> >> > >> >> b.reserveCapacity(10) // force a reallocation > >> >> > >> >> // now false, because memberwise comparison and nan != nan, > >> >> // violating the reflexivity requirement of Equatable: > >> >> a == b > >> >> > >> >> > >> >> Maybe we could go through and special-case all the places in the > >> standard > >> >> library that rely on this, accounting for the floating point behavior > >> >> (possibly reducing performance as a result). But we shouldn't expect > >> users > >> >> to. > >> >> > >> > > >> > I was not thinking about the issue illustrated above, but this is > >> > definitely problematic to me. > >> > > >> > To be clear, this proposal promises that `[0 / 0 as Double]` will be > made > >> > to compare unequal with itself, yes? > >> > >> Nope. > >> > >> As you know, equality of arrays is implemented generically and based on > >> the equatable conformance of their elements. Therefore, two arrays of > >> equatable elements are equal iff the conforming implementation of > >> Equatable's == is true for all elements. > >> > >> > It is very clear that here we are working with a concrete FP type and > >> > not in a generic context, and thus all IEEE FP behavior should apply. > >> > >> I suppose that's one interpretation, but it's not the right one. > >> > >> If this were C++, it would be different, because of the way template > >> instantiation works: in a generic context like the == of Array, the > >> compiler would look up the syntactically-available == for the elements > >> and use that. But Swift is not like that; static lookup is done at the > >> point where Array's == is compiled, and it only finds the == that's > >> supplied by the Element's Equatable conformance. > >> > >> This may sound like an argument based on implementation details of the > >> language, and to some extent it is. But that is also the fundamental > >> nature of the Swift language (and one for which we get many benefits), > >> and it is hopeless to paper over it. For example, I can claim that all > >> doubles are equal to one another: > >> > >> 9> func == (lhs: Double, rhs: Double) -> Bool { return true } > >> 10> 4.0 == 1.0 > >> $R2: Bool = true > >> 11> [4.0] == [1.0] // so the arrays should be equal too! > >> $R3: Bool = false > >> > >> Another way to look at this is that Array is not a numeric vector, and > >> won't be one no matter what you do ([1.0] + [2.0] => [1.0, 2.0]). So it > >> would be wrong for you to expect it to reflect the numeric properties of > >> its elements. > >> > >> >> This is a bump in the rug – push it down in one place, it pops up in > >> >> another. I feel like this proposal at least moves the bump to where > >> fewer > >> >> people will trip over it. I think it highly likely that the > >> intersection of > >> >> developers who understand enough about floating point to write truly > >> >> correct concrete code, but won’t know about or discover the > documented > >> >> difference in generic code, is far smaller than the set of people who > >> hit > >> >> problems with the existing behavior. > >> >> > >> > > >> > So, to extend this analogy, I'd rather say that the bump is not in the > >> rug > >> > [Comparable] but rather in a section of the floor [FP NaN]. The rug > might > >> > overlie the bump, but the bump will always be there and people will > find > >> it > >> > as they walk even if they don't immediately see it. > >> > >> Correct. > >> > >> > If we don't want people to trip over the bump while walking on the > >> > rug, one very good alternative, IMHO, is to shape the rug so that it > >> > doesn't cover the bump. > >> > >> At what cost? > >> > >> More specifically: why is it the right behavior, for our audience, to > >> trap when Equatable comparison happens to encounter NaN? Will this not > >> simply "crash" programs in the field that otherwise would have "just > >> worked?" > >> > >> > My purpose in exploring an alternative design is to see if it would be > >> > feasible for non-FP-aware comparison operators to refuse to compare > NaN, > >> > rather than giving different answers depending on context. > >> > >> So... to be clear, this is still different behavior based on context. > >> Is this not just as confusing a result? > >> > >> let nan = 0.0 / 0.0 > >> print(nan == nan) // false > >> print([nan] == [nan]) // trap > >> > > > > No, in my alternative proposal: > > > > ``` > > let nan = 0.0 / 0.0 > > print(nan == nan) // trap > > print([nan] == [nan]) // trap > > print(nan &== nan) // false > > print([nan] &== [nan]) // false > > ``` > > Oh, that's an interesting approach. Now you are asking people to > translate the == in numeric code. Yes. Specifically, in floating point code. I guess that's the part about shaping the rug not to cover the bump. IEEE does not require `==` to be the spelling of the quiet equality comparison operator, and it does specifically describe a comparison operator that behaves identically to what I propose as the trapping `==` (which, I believe, is actually spelled in the IEEE standard as `==`). I guess I'd want to hear what Steve > Canon has to say about that. > > It still begs all the questions I've asked above, though. Should I > repeat them? > > >> > I now strongly believe that this may make for a design simultaneously > >> > _less_ complex *and* _more_ comprehensive (as measured by the > >> > flatness-of-rug metric). > >> > >> I'm certainly willing to discuss it, but so far it doesn't seem like > >> you've been willing to answer the central questions above. > >> > >> -- > >> -Dave > >> > > -- > -Dave >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution