> > On Apr 13, 2017, at 3:23 PM, John McCall via swift-evolution > <swift-evolution@swift.org> wrote: > >> On Apr 13, 2017, at 4:17 PM, Ben Cohen via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> ComparisonResult Conveniences >> >> There are a few conveniences we could consider providing to make >> ComparisonResult more ergonomic to manipulate. Such as: >> >> // A way to combine orderings >> func ComparisonResult.breakingTiesWith(_ order: () -> ComparisonResult) -> >> ComparisonResult >> >> array.sort { >> $0.x.compare($0.y) >> .breakingTiesWith { $0.y.compare($1.y) } >> == .orderedAscending >> } > > The really nice thing about compare being an operator is that you can very > nicely combine it with an operator here and get a much more light-weight > syntax for chained comparisons, e.g.: > > struct MyPoint : Comparable { > var x, y, z: Double > func compare(_ other: MyPointer) -> ComparisonResult { > return self.x <=> other.x || self.y <=> other.y || self.z <=> other.z
Wow, this is elegant! > } > } > > as opposed to, I guess, > return self.x.compare(other.x).breakingTiesWith { > self.y.compare(other.y).breakingTiesWith { self.z.compare(other.z) } } > > But this is mostly useful for defining custom comparisons, so perhaps it's > not worth having to paint a bikeshed for <=> and whatever the combining > operator is. For the record, I would strongly prefer `<=>` to an instance `compare` method. That said, I’d also prefer a static `compare` function to the asymmetric instance method if the spelling `compare` were absolutely desired. It’s probably worth noting somewhere that an instance `compare` method performs dynamic dispatch on the left-hand argument while a static function (as well as the current operators `==` and `<`) perform static dispatch. I realize NSObject set a precedent with `isEqual:` and `compare:` instance methods, but I’m not convinced that’s necessarily the best design. If dynamic dispatch is desired, an implementation can always delegate to such a method. > > Also, in this example: >> // A way to combine orderings >> func ComparisonResult.breakingTiesWith(_ order: () -> ComparisonResult) -> >> ComparisonResult >> >> array.sort { >> $0.x.compare($0.y) >> .breakingTiesWith { $0.y.compare($1.y) } >> == .orderedAscending >> } > Requiring this last "== .orderedAscending" seems like a tragic failure of > ergonomics. I understand that sorting doesn't actually require a tri-valued > comparison, but is it really worth maintaining two currencies of comparison > result over that? Are there any types that can answer '<' substantially more > efficiently than they can answer 'compare'? And I assume this is related to > why you've kept < in the protocol. I would strongly support replacing (T, T) -> Bool with (T, T) -> ComparisonResult variants. The areInIncreasingOrder variant is confusing at the call-site since the definition must be consulted to determine which order the comparison expects. The areInIncreasingOrder implementation is very dirty when an == result is desired. This is especially bad if we expect other authors to mirror this API: if !areInIncreasingOrder(a, b) && !areInIncreasingOrder(b, a) { // equal! } Not only is this unintuitive, but it is also less performant in many cases. > > John. > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution