[swift-evolution] [Draft][Proposal] Formalized Ordering

Xiaodi Wu xiaodi.wu at gmail.com
Mon Jul 25 01:26:03 CDT 2016


On Mon, Jul 25, 2016 at 1:12 AM, Brent Royal-Gordon via swift-evolution <
swift-evolution at swift.org> wrote:

> > 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.
>

Agreed very much. Although, any "friendly" `<=>` can be derived from what
you call `<===>` and `==`.

I've been playing with a variation where I have a "friendly" equivalence
relation `==?` (returns `Bool?` so that it's `nil` when there's a argument
that doesn't make sense to compare) and a finer equivalence relation `===`
as protocol requirements on Equatable, with a generic `==` defined as `{
return (lhs ==? rhs) ?? (lhs === rhs) }`. In that model, traditional
comparison operators can be synthesized from a total ordering (which you
call `<===>` and the original proposal calls `<=>`) by first consulting the
value of the friendly `==?` to determine if two operands are the same.

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
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160725/4805633a/attachment.html>


More information about the swift-evolution mailing list