To begin with I have this code, which works totally fine:
protocol ComponentAccessible {}
extension ComponentAccessible {
func components<each T>(_ keyPaths: repeat KeyPath<Self, each T>) -> (repeat each T) {
{ (repeat self[keyPath: each keyPaths]) }
}
}
// MARK: - Usage
extension URL: ComponentAccessible {}
url.components(\.scheme, \.host, \.path)
But now I want to try a slightly different approach (making use of PartialKeyPath
):
extension ComponentAccessible {
func components<each T>(_ keyPaths: PartialKeyPath<Self>...) -> (repeat each T) {
keyPaths.map { self[keyPath: $0] }
}
}
This doesn't compile, because I'm returning an array, not a tuple.
Firstly, this is not type-safe. PartialKeyPath
doesn't know the type of the value of the key path, only the type of the root. So to be able to return (repeat each T)
, you need to cast.
Secondly, the type of keyPaths
, PartialKeyPath<Self>...
, is not a pack expansion. It needs to be a pack expansion for you to be able to return a tuple with a matching number of elements.
So you need the type pack each T
to appear somewhere in the type of keyPaths
, so that you can write a pack expansion for the type of keyPaths
, i.e. the caller has to pass in the types they want one way or another.
At this point, you should realise that you should just keep using KeyPath<Root, Value>
, because you can directly use each T
there. If you want a "syntax exercise", you can take pairs of a PartialKeyPath<Self>
and a metatype.
extension ComponentAccessible {
func components<each T>(keyPaths: repeat (PartialKeyPath<Self>, (each T).Type)) -> (repeat each T) {
(repeat self[keyPath: (each keyPaths).0] as! each T)
}
}
You also cannot return a tuple of Any
s. While you can "trick" the compiler into thinking you are using a type pack by writing a type alias, the compiler will still "see what you are doing" later down the line. These don't work:
typealias A<X> = Any
typealias PKP<X, Y> = PartialKeyPath<X>
// now you can write (repeat A<each T>) to mean a variadic tuple of Anys, but...
extension ComponentAccessible {
// the compiler can see that you are not using T anywhere,
// and no, you cannot add a dummy metatype parameter with a default value
// parameter pack parameters cannot have default values
func components<each T>(keyPaths: repeat PKP<Self, each T>) -> (repeat each A<each T>) {
(repeat self[keyPath: each keyPaths])
}
// the compiler cannot form the constraint that keyPaths should have the same shape as the return type
func components<each T>(keyPaths: repeat PKP<Self, each T>) -> (repeat each T) {
(repeat self[keyPath: each keyPaths] as! each T)
}
}