The only other alternative I can think of is to include a notion of unordered to our comparisons…
What if we made <=> return an optional comparison result, with nil meaning unordered (which is how Ruby does it)? For == and <, we still require a strict total ordering (i.e. they would not be optional). Most of the time, <=> and <, == would have the same comparison (<=> could have a default implementation based on == and <), but in the case of floating point it would be slightly different: <=> would return nil for anything involving NaN (in full accordance with IEEE 754), while < and == would somehow jam it into a strict total order (preferably with Nan == NaN so we don’t break collections). Thanks, Jon > On Apr 24, 2017, at 8:25 PM, Xiaodi Wu <[email protected]> wrote: > > On Mon, Apr 24, 2017 at 9:06 PM, Jonathan Hull via swift-evolution > <[email protected] <mailto:[email protected]>> wrote: > As I am thinking about it more, this means that for == and < > > NaN == NaN > -0 == +0 > +Inf < NaN > > Since this would break from IEEE, > > Yeah, as Steve mentioned, it's a huge deal to break from IEEE rules. Both the > existing design and Dave's proposed design jump through huge hoops to > preserve the behavior of these operators and reconcile them with the rest of > Swift. The biggest weakness of my alternative idea as written up earlier is > that it cannot preserve the spelling of these operators but requires a minor > adjustment (`&`). It's simply a no-go to move `==` to `compare(with: other, > using: .ieee)`. No one will write numerics algorithms like that. > > I think we should also consider taking the opportunity to make == and < work > with a default tolerance. That is, 'a == b' would check that (a - b) < > epsilon for some reasonable choice of epsilon that works for common usage. I > know this would make the hash function harder to write, but I think it is > worthwhile. > > Then, as I mentioned before, people who need strict IEEE conformance would > explicitly declare that need by using 'compare(with: other, using: .IEEE)' or > whatever we want to call that metric. We can even provide metrics for > different IEEE levels, if desired. > > We could also provide a function ‘tolerance(_:Self) -> Comparable.Metric’ on > FloatingPoint which returns a comparison metric that defines equality as (a - > b) < tolerance, for those who want to specify a specific tolerance for their > use-case/algorithm... > > Thanks, > Jon > > > > > On Apr 23, 2017, at 7:18 AM, Jonathan Hull via swift-evolution > > <[email protected] <mailto:[email protected]>> wrote: > > > > There is one more option which hasn’t really been considered: > > > > • == and != are tied to the Equatable protocol, which is essentially the == > > operation. > > • <, <=, >, >= are tied to the Comparable protocol (which is kept the same > > except for minor changes/additions listed below) > > • Hashable still requires Equatable > > • There is a new ComparisonMetric concept which lets an algorithm specify > > exactly how the comparison is done (see below) > > > > > > Tl;dr: There are different definitions of ‘comparison’ which make sense in > > different domains… so let’s make it explicit so it doesn’t surprise anyone. > > > > The question then becomes, which metric should be the default (i.e. the one > > defined by ‘<‘ and ‘==‘), and the answer is: the one which lets us use > > floats/doubles in dictionaries and sets. People and algorithms which need > > full IEEE correctness can use a different metric which specifically > > guarantees it. They can even build their own metric if needed. > > > > > > ====The Design==== > > // (Note: I wrote this code in mail, so it may not compile) > > > > > > //This defines the result of a comparison. It would ideally be nested in > > the protocol below if that becomes possible. > > enum ComparisonResult : Int { > > case ascending = -1 > > case equal = 0 > > case descending = 1 > > } > > > > protocol Comparable { > > typealias Metric = (Self, Self) -> ComparisonResult //Give ourselves > > an easy way to refer to this function type > > > > var defaultMetric: Metric > > static func <(lhs: Self, rhs: Self) -> Bool > > } > > > > extension Comparable { > > //Not shown: We would define <=, etc… plus ≤,≥,and ≠ (because, hey, > > it is my proposal) > > > > func compare(with other: Self, using metric: Metric) -> > > ComparisonResult { > > return metric(self, other) > > } > > > > func compare(with other: Self) -> ComparisonResult { > > return self.defaultMetric(self, other) > > } > > > > static func <=> (lhs: Self, rhs: Self) -> Int { > > return self.defaultMetric(lhs, rhs).rawValue > > } > > > > var defaultMetric: Metric { > > return { lhs, rhs in > > if lhs == rhs { > > return .equal > > } else if lhs < rhs { > > return .ascending > > } > > return .descending > > } > > } > > } > > > > ============ > > > > Then for Double, we would make a second metric for IEEE compliant (or > > multiple for different levels) > > > > extension Double : Comparable { > > > > static func < (lhs: Self, rhs: Self) -> Bool { > > //define using best for dictionaries / sets / layman > > understanding > > } > > > > static func == (lhs: Self, rhs: Self) -> Bool { > > //define using best for dictionaries / sets / layman > > understanding > > } > > > > static var IEEEcompare:Comparable.Metric { > > //return function here that does full IEEE comparison > > } > > > > } > > > > Then we can call ‘myDouble.compare(with: otherDouble, using: .IEEEcompare)’ > > when needed. > > > > > > Thanks, > > Jon > > > > > > > >> On Apr 22, 2017, at 9:58 PM, Chris Lattner via swift-evolution > >> <[email protected] <mailto:[email protected]>> wrote: > >> > >> On Apr 22, 2017, at 6:06 PM, Xiaodi Wu <[email protected] > >> <mailto:[email protected]>> wrote: > >>> but my quick reaction to `&==` is that it would make me quite nervous to > >>> have `==` not bound to 754-equals as it is in essentially every other > >>> language. In particular, I worry about the risk of people porting > >>> numerical code that depends on isnan(x) <—> !(x < y) in non-obvious ways > >>> that they are unlikely to test. I’ll try to follow up with more detailed > >>> thoughts tomorrow. > >>> > >>> Indeed, it makes me a little nervous too. That said, `==` being either > >>> bound or not bound to 754 depending on the context is what makes me even > >>> more nervous. > >>> > >>> I was once adamantly against a new spelling for `==`, but on > >>> reconsideration it's clear to me that few if any numerical recipes can be > >>> ported verbatim from C-like languages and we should probably not > >>> encourage people to do so. Already, `+` needs to be rewritten as `&+`, > >>> `<<` probably should be rewritten as `&<<` (I still haven't had enough > >>> time to think about this), and the bitwise operators have differing > >>> precedences that require careful proofreading. > >> > >> > >> I haven’t been following this proposal or discussion closely, but it seems > >> to me that there are a few workable approaches with different tradeoffs: > >> > >> 1. The strictly correct but user hostile approach: > >> > >> * == and != are tied to the Equatable protocol, which is essentially the > >> == operation. > >> * <, <=, >, >= are tied to the Comparable protocol, which is essentially > >> the <=> operation. > >> * Hashable doesn’t require equatable, it requires a related > >> StrictlyEquatable protocol. > >> * StrictlyEquatable refines Equatable (with no other requirements, it is > >> just a marker protocol), in which case FP types can’t conform to it, and > >> thus can’t participate as dictionary keys > >> > >> => This approach sucks because you can’t have Set<Float>, or > >> Dictionary<Float, String>. > >> > >> 2. The strictly correct but somewhat user hostile approach: > >> > >> * == and != are tied to the Equatable protocol, which is essentially the > >> == operation. > >> * <, <=, >, >= are tied to the Comparable protocol, which is essentially > >> the <=> operation. > >> * Hashable doesn’t require equatable, it requires a related > >> StrictlyEquatable protocol. > >> * StrictlyEquatable doesn’t refine Equatable: it has a different > >> requirement, and FP types can therefore implement both Equatable and > >> StrictlyEquatable. > >> > >> => This approach is suboptimal because implementing your own type requires > >> you to implement the <=> operation, as well as the StrictlyEquatable > >> protocol, both. > >> > >> 3. The user friendly but incorrect model: > >> > >> * == and != are tied to the Equatable protocol, which is essentially the > >> == operation. > >> * <, <=, >, >= are tied to the Comparable protocol, which is essentially > >> the <=> operation. > >> * Hashable is defined in terms of Equatable. > >> > >> => This is easy (types just have to define <=>), but fails for FP types. > >> > >> > >> I don’t think that this proposal is acceptable as written. I think it is > >> really bad that abstracting a concrete algorithm would change its behavior > >> so substantially. I don’t care about SNaNs, but I do care about the > >> difference between +0/-1 and secondarily that of NaN handling. It seems > >> really bad that generalizing something like: > >> > >> func doThing(a : Double, b : Double) -> Bool { > >> …. > >> return a != b > >> } > >> > >> to: > >> > >> func doThing<T : FloatingPoint> (a : T, b : T) -> Bool { > >> …. > >> return a != b > >> } > >> > >> would change behavior (e.g. when a is -0.0 and b is +0.0). Likewise, "T > >> : Equatable". > >> > >> -Chris > >> > >> > >> _______________________________________________ > >> swift-evolution mailing list > >> [email protected] <mailto:[email protected]> > >> https://lists.swift.org/mailman/listinfo/swift-evolution > >> <https://lists.swift.org/mailman/listinfo/swift-evolution> > > > > _______________________________________________ > > swift-evolution mailing list > > [email protected] <mailto:[email protected]> > > https://lists.swift.org/mailman/listinfo/swift-evolution > > <https://lists.swift.org/mailman/listinfo/swift-evolution> > > _______________________________________________ > swift-evolution mailing list > [email protected] <mailto:[email protected]> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
