> On 16 Apr 2017, at 18:35, Karl Wagner <karl.sw...@springsup.com> wrote: > >> >> On 16 Apr 2017, at 05:32, Dave Abrahams via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >> >> on Sat Apr 15 2017, Xiaodi Wu <swift-evolution@swift.org >> <mailto:swift-evolution@swift.org>> wrote: >> >>> On Sat, Apr 15, 2017 at 3:12 PM, Dave Abrahams via swift-evolution < >>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >>> >>>> >>>> on Thu Apr 13 2017, Xiaodi Wu <swift-evolution@swift.org >>>> <mailto:swift-evolution@swift.org>> wrote: >>>> >>>>> Getting this sorted out is definitely a worthwhile effort. I do have >>>>> thoughts about this proposal: >>>>> >>>>> I continue to have reservations about an identical spelling (e.g. `==`) >>>>> giving two different answers with the same values of the same type, >>>>> depending on the generic context. It is a very *clever* design, but it is >>>>> also a very *subtle* behavior that I can see leading to much confusion >>>> and >>>>> befuddlement for any user who is not well versed *both* in the >>>> intricacies >>>>> of IEEE floating point *and* in the intricacies of Swift. >>>> >>>> I can't help but think that the concern over confusion here is not >>>> informed by any realistic situations. Please describe >>>> >>> >>> To be clear, I'm not claiming that my concerns about the proposal outweigh >>> my enthusiasm for it. >>> >>> But here, the confusion I'm concerned about stems from the essential >>> conclusion by the proposal authors that types (including, but not >>> necessarily only limited to FP types) which are ordinarily compared in a >>> way that treats certain "special values" differently must also present an >>> alternative notion of comparison that accounts for all possible >>> values. >> >> That may be a conclusion, but it's not an assumption. For example, it's >> totally reasonable that there is a value of Int (i.e. 0) for which the >> requirements of division don't hold. We say that 0 is outside the >> domain of / when used as a divisor, and we tried to get away with saying >> that NaN was outside the domain of ==. However, it's also reasonable to >> trap on integer division by zero. >> >> What we have is a situation where values that “misbehave” when given >> IEEE semantics occur in normal code and are expected to interoperate >> with other floating point values under normal circumstances (such as >> when sorting), and not only interoperate but give reasonable results. >> >> Now, having thought about this a bit more myself, here is a real case >> where confusion might occur: >> >> if values.contains(.NaN) { >> print(values.filter { $0 != .NaN }) // Surprise, NaN is printed! >> } >> >> I find this result truly loathsome, but it seems to me that the only >> reasonable cure is giving == equivalence relation semantics under all >> circumstances. > > > The thing that’s bad about it is that we silently pick different operators > for different contexts. With Swift’s heavy use of abstraction layering, you > often can’t really tell what the context is (if it even has meaning at all). > > I’ve been thinking about Swift architectural patterns, and one pattern that I > think of as being a good fit for the language is this idea of wrapping > operations on protocol-types as generic structs (for example, the way we do > FilterCollection, LazyMapCollection, and he various String views in the > standard library), providing transformed “views” of the object with a common > base protocol (which will hopefully get optimised away). I wonder if we > couldn’t apply a similar idea here… > > So basically, every FloatingPoint will expose another pseudo-FloatingPoint > type which differs from its base object only in its interpretation of > “Comparable” operators. The type-system would enforce that you are comparing > them consistently. > > protocol FloatingPoint: Comparable { > > /// A FloatingPoint with comparison quirks > /// > associatedtype StandardComparable: FloatingPoint > var comparable: StandardComparable { get } > > /// A FloatingPoint which compares according to IEEE level <whatever> > /// > associatedtype IEEEComparable: FloatingPoint = Self > var ieeeComparable: IEEEComparable { get } > } > extension FloatingPoint where IEEEComparable == Self { > var ieeeComparable: Self { return self } > } > > struct Float: FloatingPoint { > /* IEEE comparison */ > static func compare(_: Float, to: Float) -> ComparisonResult { ... } > > /* Quirky Float where .Nan == .Nan, +0.0 == -0.0 etc... */ > struct StandardComparable: FloatingPoint { > static func compare(_: StandardComparable, to: StandardComparable) -> > ComparisonResult { ... } > } > var comparable: StandardComparable { return StandardComparable(self) } > } > > The idea is that the invisible context-sensitive comparison quirks would > become visible: > > if values.contains(.NaN) { // uses IEEE rules, so is always false > print(values.filter { $0 != .NaN }) > } > > if values.contains(where: { $0.comparable == .NaN }) { // opt-in to stdlib > quirks > print(values.filter { $0.comparable != .NaN }) // no NaNs > } > > - Karl
Oh, and: Float.StandardComparable.IEEEComparable = Float, and Float.StandardComparable.StandardComparable = Self ...so they wouldn’t recurse. You could just flip between views with “.comparable” or “.ieeeComparable”. - Karl
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution