[swift-evolution] [Idea] Bringing the partial/total ordering distinction into Comparable
Chris Lattner
clattner at apple.com
Sun Apr 24 16:11:11 CDT 2016
On Apr 23, 2016, at 6:28 PM, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:
> Simple and straightforward, but not actually accurate. In a strict total order, all elements are ordered, but that's not true of the current Comparable. For instance, floating-point NaNs are not ordered.
> In short, I propose we:
>
> * Introduce a new `<=>` operator which implements a strict total ordering on the Comparable type. Rather than returning a `Bool`, it returns a new `Order` type which is similar to `NSComparisonResult`. This provides a semantic hint that non-ordering is not an option for `<=>`.
I’m generally in favor of moving to a comparison model like you propose, where we get a spaceship operator, an “Order” enum with less/equal/greater members, and have all the other operators be derived from that.
However, I don’t understand how that would help for floating point NaN behavior. Wouldn’t you have to add a fourth member to the enum (“unordered’) that all clients would have to handle? An approach like that could make sense.
-Chris
> Here's what the new `Comparable` might look like:
>
> public enum Order {
> case firstEarlier
> case bothEqual
> case firstLater
> }
>
> /// Instances of conforming types can be compared using relational operators.
> ///
> /// Comparable includes both a total order, which sorts all possible values,
> /// and a partial order, which compares only "normal" or "common" values.
> /// The partial order may consider some elements "unordered" and return `false`
> /// for all operations.
> ///
> /// The `<=>` operator implements the total order; the others implement the
> /// partial order. You may define only the total order, and `Comparable` will
> /// provide default implementations which use it. You may also define both the
> /// `<=>` operator and the `<>` "unordered" operator, and Comparable will
> /// provide default implementations for the rest of the partial order which them.
> /// You may also choose to implement the `<`, `>`, `<=`, `>=`, `==`, and
> /// `!=` operators to completely customize the implementation.
> public protocol Comparable : Equatable {
> /// A [total order](http://en.wikipedia.org/wiki/Total_order#Strict_total_order)
> /// over instances of `Self`. In a total order, no element is permitted to be
> /// unordered relative to any other.
> @warn_unused_result
> func <=> (lhs: Self, rhs: Self) -> Order
>
> /// Returns `true` if, to partial order operators like `<` and `==`, `lhs` is
> /// unordered relative to `rhs`.
> @warn_unused_result
> func <> (lhs: Self, rhs: Self) -> Bool
>
> /// Returns `true` if `lhs` is less than `rhs`. Should be consistent with `<=>` except
> /// when the elements are unordered relative to each other.
> @warn_unused_result
> func < (lhs: Self, rhs: Self) -> Bool
>
> /// Returns `true` if `lhs` is greater than `rhs`. Should be consistent with `<=>` except
> /// when the elements are unordered relative to each other.
> @warn_unused_result
> func > (lhs: Self, rhs: Self) -> Bool
>
> /// Returns `true` if `lhs` is less than or equal to `rhs`. Should be consistent with `<=>`
> /// except when the elements are unordered relative to each other.
> @warn_unused_result
> func <= (lhs: Self, rhs: Self) -> Bool
>
> /// Returns `true` if `lhs` is greater than or equal to `rhs`. Should be consistent with `<=>` except
> /// when the elements are unordered relative to each other.
> @warn_unused_result
> func >= (lhs: Self, rhs: Self) -> Bool
> }
>
> Some APIs on Order which might be useful:
>
> public extension Order {
> /// Returns the equivalent order for the two arguments reversed.
> func reversed() -> Order {…}
> /// Returns `x` and `y` reordered according to `self`, with the earlier one first.
> func reorder<T>(_ x: T, _ y: T) -> (T, T) {…}
> /// Returns `x` and `y` reordered with the earlier one first.
> static func reorder<T: Comparable>(_ x: T, _ y: T) -> (T, T) {…}
> }
>
> Alternate designs:
>
> * The `<>` operator is arguably not very obvious, or too confusable with some languages' use of that operator for "not equals". It could instead be a different operator, an instance method, or a class method.
> * It might make sense to instead use `<>` to say "is comparable" and `!<>` to say "is incomparable".
> * It may also be better to define Comparable such that certain *values* are incomparable with any value, rather than certain *pairs* of values being incomparable. If so, we would want an `isIncomparable` property instead of a method or function. That works for `FloatingPoint`, but it might not suit other types. (For instance, with the `<>` operator in place, `String.Index` could be made incomparable with indices from other strings, but all `String.Index`es would still have a total order. That design wouldn't be possible with an `isIncomparable` property.)
> * The `<=>` operator is common from other languages, but it might still be too jargony. One interesting design for this would be to expose the total order as a method on `Comparable` which is used as an implementation hook for an `Order.init(_:_:)` initializer.
> * The cases of Order are highly bikesheddable. I like these names more than `ascending` and `descending` because I have an easier time understanding what they mean, but others might disagree.
> * I'm also toying with the idea that the partial order, which includes `==`, may have a looser definition of equality than the total order; this would mean that, for instance, `String`'s total order could fall back to `UnicodeScalar.value` comparison to distinguish between strings which have equal graphemes. I'm not sure how useful that would be in practice, though.
>
> Any thoughts?
>
> --
> Brent Royal-Gordon
> Architechies
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
More information about the swift-evolution
mailing list