Почему универсальный тип не участвует в перегрузке метода через ограничение дальше по цепочке вызовов? - PullRequest
0 голосов
/ 11 января 2019

Swift позволяет перегрузить универсальные методы с помощью ограничений, наложенных на переданные универсальные типы. Если вы используете это с конкретными типами, то переданный тип будет участвовать в этой перегрузке, и ограничения будут выведены из этого типа.

Как только универсальный метод делегирует другому с разрешением перегрузки, ограничения больше не могут быть выведены и вместо этого будут использоваться ограничения, уже наложенные на тип сверху.

protocol Conformance {}

extension String : Conformance {}

// #1
func baseMethod<T>(_ value: T) {
    let isConforming = T.self is Conformance.Type
}

// #2
func baseMethod<T>(_ value: T) where T : Conformance {
    let isConforming = T.self is Conformance.Type
}

func delegatingMethod<T>(_ value: T) {
    baseMethod(value)
}

func run() {
    // Calls #2, isConforming = true
    baseMethod(String())
    // Calls #1, isConforming = true
    delegatingMethod(String())
}

Я предполагаю, что это так, что у вас есть достаточная информация о типе с сайта вызова о том, какие ограничения применимы независимо от того, где используется универсальный тип, но, похоже, это сильно и искусственно ограничивает полезность перегрузки ограничением.

Есть ли известные обходные пути для этой странности? То, что подражает этому, было бы чрезвычайно полезно.

1 Ответ

0 голосов
/ 11 января 2019

Swift позволяет перегрузить универсальные методы с помощью ограничений, наложенных на переданные универсальные типы.

Да ... но очень ясно, что это статическая перегрузка , а не динамическое переопределение . Он основан на типах, которые могут быть проверены во время компиляции.

func delegatingMethod<T>(_ value: T) {
    baseMethod(value)
}

Мы сейчас компилируем это, и нам нужно записать это как конкретный статический вызов функции, возможно встроенный, в двоичный файл. Что мы знаем о T? ничего не знаем о T, поэтому любое предложение where завершится неудачей.

Мы даже не знаем, как вызывается эта функция, потому что вызов может исходить из другого модуля или модуля компиляции. Хотя в принципе он может иметь различную семантику в зависимости от уровня доступа, например, если одна версия используется, когда она является закрытой, и все вызовы могут быть оценены, а другая используется, когда она общедоступна, это будет действительно ужасным источником ошибок.

То, что вы запрашиваете, - это delegatingMethod отложить свое решение о том, какой вызов функции сделать до времени выполнения. Это не то, как работают дженерики. Более того, вы просите, чтобы все предложения where были закодированы где-то в двоичном файле, чтобы их можно было оценить во время выполнения. Также не как работают дженерики. Это потребует гораздо более динамичной системы диспетчеризации, чем Swift хочет реализовать. Это не невозможно; это просто совершенно другое животное, и оно предотвращает множество оптимизаций.

Такое ощущение, что вы пытаетесь заново изобрести наследование классов с помощью протоколов и обобщений. Ты не можешь Это разные решения и разные функции. Классовое наследование является фундаментально динамичным. Протоколы и дженерики в основном статичны. Если вы хотите динамическую диспетчеризацию на основе определенных типов, используйте классы.

...