Вопрос или проблема
Предложение по параметрам пакетов в эволюции Swift начинается с раздела Мотивация, в котором упоминается следующий знакомый недостаток:
func < (lhs: (), rhs: ()) -> Bool
func < <A, B>(lhs: (A, B), rhs: (A, B)) -> Bool where A: Comparable, B: Comparable
func < <A, B, C>(lhs: (A, B, C), rhs: (A, B, C)) -> Bool where A: Comparable, B: Comparable, C: Comparable
Проблема здесь в том, что для реализации сравнения кортежей мы должны реализовать это для 2-кортежей, затем реализовать это снова для 3-кортежей и так далее. Кроме того, это повторение означает, что нам нужно отказаться от этого после какого-то произвольного максимального размера кортежа (6-кортежи).
Мой вопрос: как именно пакеты параметров Swift решают эту проблему? Посматривая в заголовки для Swift 6, я не вижу, что объявление оператора <
изменилось по сравнению с предыдущими версиями Swift. (На самом деле, я не вижу ни одного использования пакетов параметров в заголовках Swift вообще.) И в любом случае (и что более важно), пакеты параметров вовсе не о кортежах; они касаются вариадиков.
Я сразу вижу, как мы можем реализовать сравнение между двумя вариадическими списками сравнимых значений:
func isLessThan<each T: Comparable>(lhs: repeat each T, rhs: repeat each T) -> Bool {
for pair in repeat (each lhs, each rhs) {
if pair.0 >= pair.1 {
return false
}
}
return true
}
И мы можем вызвать это вот так:
let result = isLessThan(lhs: 1, "a", rhs: 2, "b")
Отлично — но это не кортежи. Это вариадические списки. Основная проблема заключается в том, что нет способа динамически создать вариадический список: вы не можете преобразовать кортеж (или последовательность) в вариадический список.
Так каким образом пакеты параметров решают постановку проблемы?
Вы можете написать сигнатуру функции таким образом, чтобы она работала с кортежами:
func isLessThan<each T: Comparable>(lhs: (repeat each T), rhs: (repeat each T)) -> Bool
Я бы хотел рассматривать развертывание пакета как repeat each T
в виде T1, T2, T3, ..., Tn
. Тогда естественно, (repeat each T)
является кортежем, (T1, T2, T3, ..., Tn)
.
Тело функции может оставаться без изменений.
Замечание: планируется, что кортежи будут Comparable
.
Ответ или решение
Параметрические пакеты в Swift действительно решают проблему избыточного кода, связанного с реализацией операций сравнения для кортежей различной длины. Проблема, обозначенная в предложении по эволюции Swift (Swift evolution proposal) 0393, заключается в том, что для определения операций сравнения для кортежей разной длины, таких как 2-кортежи, 3-кортежи и так далее, необходимо создавать множество дублирующих функций. Это приводит к ухудшению читаемости кода и необходимости вручную ограничивать длину кортежей, поскольку до определенного момента не существует универсального решения.
Как решают проблемы параметрические пакеты
Параметрические пакеты (parameter packs) в Swift позволяют избежать дублирования кода за счет возможности обобщенного описания группы типов. Например, вместо определения отдельных функций isLessThan
для каждого размера кортежей можно описать одну универсальную функцию, которая будет работать с произвольным числом элементов.
Пример реализации функции с использованием параметрических пакетов:
func isLessThan<each T: Comparable>(lhs: (repeat each T), rhs: (repeat each T)) -> Bool {
for pair in repeat (each lhs, each rhs) {
if pair.0 >= pair.1 {
return false
}
}
return true
}
В этом примере repeat each T
представляет собой механизм, который позволяет работать с кортежами произвольной длины. Запись (repeat each T)
фактически создаёт кортеж, который может содержать значения разных типов, но при этом все они должны соответствовать протоколу Comparable
. Это позволяет компилировать код, который может работать с кортежами разных размеров, без необходимости явно определять каждую комбинацию.
Резюме
Таким образом, параметрические пакеты в Swift не только упрощают работу с типами, но и значительно улучшают возможность написания универсального, чистого и поддерживаемого кода. Использование параметрических пакетов устраняет проблему дублирования и ограниченного количества поддерживаемых типов, что открывает новые горизонты для разработки обобщённых функций, таких как сравнение кортежей с различным количеством элементов. Хотя на текущий момент прямой реализации таких функций для кортежей в Swift не существует, концепция пакетов создаёт потенциал для добавления таких возможностей в будущем.