-1

I'm trying to implement a convenience Collection.sorted(by: KeyPath) function.

So far, it works if do

func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>) -> [Element] {
    return sorted { lhs, rhs
        return lhs[keyPath: keyPath] < rhs[keyPath: keyPath]
    }
}

But what if I want to allow the caller to specify the actual sorting logic ? I added a callback to perform the comparison, like such (taking inspiration from the orginal sorted(_:) function signature).

func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>, _ compare: (T, T) throws -> Bool) rethrows -> [Element] {
    return try sorted { lhs, rhs in
        return try compare(lhs[keyPath: keyPath], rhs[keyPath: keyPath])
    }
}

Now, this is all works, but it means the callsite always has to specify which sorting operation to perform.

let sorted = myArray.sorted(by: \.name, <)

I'd like it to default to <, but how can I reference the < operator by default, in my function's signature ?

Skwiggs
  • 1,348
  • 2
  • 17
  • 42
  • 1
    Do what `sorted` does. You have two different functions. One accepts only Comparables and no function parameter, because it defaults to `<`. The other accepts any T and takes a function parameter. – matt Mar 20 '20 at 11:25
  • @matt oh... _duh_ It never occured to me that those were 2 separate functions – Skwiggs Mar 20 '20 at 11:28
  • Well just look at them. https://developer.apple.com/documentation/swift/array Scroll down till you get to `sorted`. There are two of them. – matt Mar 20 '20 at 11:31
  • @matt yep I've now implemented it in 2 functions, too. Is there really no way to reference a Type's static function, however ? – Skwiggs Mar 20 '20 at 12:47
  • Not sure what means. `<` is the reference to this function, as you already know. That’s why you can say `sorted(by:<)`. But it doesn’t belong to a type; it’s an operator function. – matt Mar 20 '20 at 12:51

1 Answers1

1

It is actually possible to reference the un-applied < function by wrapping it in parentheses (<) when using it as a default parameter.

func sorted<T: Comparable>(
    by keyPath: KeyPath<Element, T>, 
    _ compare: (T, T) throws -> Bool = (<)
    ) rethrows -> [Element] {
    return try sorted { lhs, rhs in
        return try compare(lhs[keyPath: keyPath], rhs[keyPath: keyPath])
    }
}

However, there is currently an issue with the compiler when doing this. Even though < doesn't throw, the compiler will still enforce you to use try at the call site.

A bug report for this was opened quite some time ago, and is still unresolved. If you run into this, please upvote it: https://bugs.swift.org/browse/SR-1534

Additionally, as pointed out in the comments, the sorted(by:) function is actually 2 different functions.

One requires Comparable and uses < internally, while the other lets you specify the sorting logic directly and thus, does not require Comparable conformance.

Therefore, this convenience sorting by keyPath would still require 2 functions.

Skwiggs
  • 1,348
  • 2
  • 17
  • 42