1

I'm writing some code where a user can select how a particular array of data is sorted. I was trying to see if I could hold the set of permissible sort properties in an enum. What I want to be able to express is something like:

import Foundation

struct MyStruct {
    let a: Int
    let b: Int
}

enum MyStructProps {
    case a, b
    
    func comparableKeyPath<T: Comparable>() -> KeyPath<MyStruct, T> {
        switch self {
            case .a: return \MyStruct.a
            case .b: return \MyStruct.b
        }
    }
}

At the moment each case returns a compiler error: key path value type 'Int' cannot be converted to contextual type 'T'.

Looking at the post Swift Generics, Constraints, and KeyPaths I would need to embed this within a sort function, so that Swift knows how to derive the type of the generic key path.

But I was curious to learn if there is a way of returning a generic keypath in my naive code?

Philip Pegden
  • 1,732
  • 1
  • 14
  • 33
  • You can probably write an `AnyComparable` (like the one in [my answer here](https://stackoverflow.com/q/70620524/5133585)), and return a `KeyPath`, but I feel that's overkill, when you could just "embed this within a sort function" as you said. – Sweeper May 25 '22 at 13:14

1 Answers1

0

If you need to work at some more intermediate level than the following, you'll need to type-erase, as Sweeper says in the comment.

Otherwise, because you can't return different types from one function, just employ generics for the intermediate steps, and have one function at the end of the process that employs multiple types.

extension Sequence where Element == MyStruct {
  func sorted(by property: Element.ComparableProperty) -> [Element] {
    switch property {
    case .a: return sorted(by: \.a)
    case .b: return sorted(by: \.b)
    }
  }
}
extension MyStruct {
  enum ComparableProperty {
    case a, b
  }
}
public extension Sequence {
  /// Sorted by a common `Comparable` value.
  func sorted<Comparable: Swift.Comparable>(
    by comparable: (Element) throws -> Comparable
  ) rethrows -> [Element] {
    try sorted(by: comparable, <)
  }

  /// Sorted by a common `Comparable` value, and sorting closure.
  func sorted<Comparable: Swift.Comparable>(
    by comparable: (Element) throws -> Comparable,
    _ areInIncreasingOrder: (Comparable, Comparable) throws -> Bool
  ) rethrows -> [Element] {
    try sorted {
      try areInIncreasingOrder(comparable($0), comparable($1))
    }
  }
}