Для начала у меня есть этот код, который работает совершенно нормально:
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)
Но теперь я хочу попробовать немного другой подход (используя PartialKeyPath
):
extension ComponentAccessible {
func components<each T>(_ keyPaths: PartialKeyPath<Self>...) -> (repeat each T) {
keyPaths.map { self[keyPath: $0] }
}
}
Это не компилируется, потому что я возвращаю массив, а не кортеж.
@RoyRodney Синтаксическое упражнение 😃
Как отметил Рой, использование такого частичного ключевого пути по своей сути не является типобезопасным. Так что это просто невозможно.
Во-первых, это не типобезопасно. PartialKeyPath
не знает тип значения ключевого пути, только тип корня. Итак, чтобы иметь возможность вернуть (repeat each T)
, нужно провести кастинг.
Во-вторых, тип keyPaths
, PartialKeyPath<Self>...
не является расширением пакета. Это должно быть расширение пакета, чтобы вы могли вернуть кортеж с соответствующим количеством элементов.
Итак, вам нужно, чтобы пакет типов each T
появлялся где-то в типе keyPaths
, чтобы вы могли написать расширение пакета для типа keyPaths
, т. е. вызывающая сторона должна так или иначе передать те типы, которые ему нужны.
На этом этапе вы должны понимать, что вам следует просто продолжать использовать KeyPath<Root, Value>
, потому что вы можете напрямую использовать each T
там. Если вам нужно «синтаксическое упражнение», вы можете взять пары PartialKeyPath<Self>
и метатипа.
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)
}
}
Вы также не можете вернуть кортеж Any
. Хотя вы можете «обмануть» компилятор, заставив его думать, что вы используете пакет типов, написав псевдоним типа, компилятор все равно «увидит, что вы делаете» позже. Это не работает:
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)
}
}
Я считаю, что проблема заключается в разнице между PartialKeyPath и KeyPath, поскольку PartialKeyPath не знает тип, на который он ссылается, что делает невозможным (или, по крайней мере, трудным) разрешение <each T> как типа. В чем причина использования PartialKeyPath?