[swift-evolution] [Draft][Proposal] Formalized Ordering
Brent Royal-Gordon
brent at architechies.com
Mon Jul 25 01:12:28 CDT 2016
> On Jul 24, 2016, at 9:06 PM, Pyry Jahkola via swift-evolution <swift-evolution at swift.org> wrote:
>
> Another possible choice would be to return .descending whenever either of the comparands were NaN, while also making <, >, <=, and >= return false in such cases. Then we wouldn't see preconditionFailures but instead produced bogus results from sort, partition etc. That's the tradeoff Haskell has chosen for its `compare` function over Double, FWIW.
That's essentially what we currently have. I think we'd like to fix it.
Honestly, I think the most natural result is that calls like `sort()` and `max()` ignore NaNs—for instance, an Array<Double> might have fewer elements if you sort it. That seems to be the behavior implied by `FloatingPoint.maximum/minimum(_:_:)`. However, it is still possible to access and use the total ordering if you need it.
This sort of suggests we should have two levels of comparisons:
* `===` and `<===>` are total.
* `==` and `<=>` may not work on, or may conflate, some values.
How to actually accomplish this is a more difficult question. The simplest solution might be something like:
protocol Equatable {
static func === (…) -> Bool
static func == (…) -> Bool
}
extension Equatable {
static func == (…) -> Bool {
return lhs === rhs
}
}
protocol Comparable: Equatable {
/// Total ordering which works on and distinguishes between all values of the type.
static func <===> (…) -> Ordering
/// "Friendly" ordering which may conflate or not work on some values of the type.
///
/// - Precondition: Neither `lhs` nor `rhs` returns `true` from `isAberration`.
static func <=> (…) -> Ordering
/// If true, this instance should be ignored when using the <=> operator.
var isAberration: Bool { get }
}
extension Comparable {
static func === (…) -> Bool {
return (lhs <===> rhs) == .same
}
static func == (…) -> Bool {
return (lhs <=> rhs) == .same
}
static func <=> (…) -> Ordering {
return lhs <===> rhs
}
var isAberration: Bool {
return true
}
}
However, this means that sorting requires two functions, not one (or that, when using a custom sorting function, you must separately pre-filter the aberrations from your data set). An alternative would be to introduce a PartialOrdering type:
enum PartialOrdering {
case ordered (Ordering)
case leftUnordered
case bothUnordered
case rightUnordered
}
// As above, except...
protocol Comparable: Equatable {
...
/// "Friendly" ordering which may not work on some values of the type.
///
/// - Precondition: Neither `lhs` nor `rhs` returns `true` from `isAberration`.
static func <=> (…) -> PartialOrdering
}
This wouldn't necessarily handle the `-0.0 == +0.0` case well, though. That *could* be handled with extra cases meaning "equal but ordered", but this is looking messier and messier.
--
Brent Royal-Gordon
Architechies
More information about the swift-evolution
mailing list