Как параметрическая упаковка Swift решает поставленную мотивационную задачу?

Вопрос или проблема

Предложение по параметрам пакетов в эволюции 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 не существует, концепция пакетов создаёт потенциал для добавления таких возможностей в будущем.

Оцените материал
Добавить комментарий

Капча загружается...