Search code examples
swiftgenericscomparabledefault-parameters

Swift - Reference default comparison function as a function parameter


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 ?


Solution

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