Перегрузка общих c функций в iOS Swift - PullRequest
2 голосов
/ 07 мая 2020

Я пытаюсь создать средство поиска, которое пытается найти несколько типов, которые передаются ему внутри замыкания.

enum SomeError: Error {
    case notInitialized
}

struct TestFinder {

    func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U {
        guard let t: T = get() else {
                throw SomeError.notInitialized
        }

        return function(t)
    }

    func getSomething<T, U, V>(_ function: @escaping (T, U) -> V) throws -> V {
        guard let t: T = get(), let u: U = get() else {
                throw SomeError.notInitialized
        }

        return function(t, u)
    }

    func getSomething<T, U, V, W>(_ function: @escaping (T, U, V) -> W) throws -> W {
        guard let t: T = get(), let u: U = get(), let v: V = get() else {
                throw SomeError.notInitialized
        }

        return function(t, u, v)
    }

    private func get<T>() -> T? {
       nil
    }
}

struct UserDetails {
    let name: String
    let roll: String
}

Я вызываю средство поиска как:

let testReturnType = try? TestFinder().getSomething(UserDetails.init)

Компилятор выдает мне ошибку:

неоднозначное использование 'getSomething'

Причина этой ошибки (из документов):

Вы можете перегрузить общую c функцию или инициализатор, указав различные ограничения, требования или и то, и другое в параметрах типа. Когда вы вызываете перегруженную функцию или инициализатор generi c, компилятор использует эти ограничения, чтобы определить, какую перегруженную функцию или инициализатор вызывать.

Но если я прокомментирую:

func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U

Все начинает работать. Это как-то связано с тем, что компилятор не может определить, какую сигнатуру функции нужно разрешить.

Какое-либо конкретное решение для этого?

Ответы [ 2 ]

1 голос
/ 08 мая 2020

Вы не совсем сосредоточились на реальной проблеме. Исключим из примера все, что не имеет отношения к делу. Это компилируется и работает, как ожидалось:

struct TestFinder {

    func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
        print("two")
    }

    func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
        print("three")
    }

    func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
        print("four")
    }

}

И здесь мы проверим это:

    func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
    TestFinder().doSomething(f) // "four"

Но если вы добавите версию с одним переданным параметром функции , все ломается:

struct TestFinder {

    func doSomething<T>(_ function: (T) -> Void) -> Void {
        print("one")
    }

    func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
        print("two")
    }

    func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
        print("three")
    }

    func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
        print("four")
    }
}

Теперь мы не можем скомпилировать, потому что версия первая рассматривается как кандидат. И действительно, если мы удалим другие версии, мы по-прежнему будем компилировать !

struct TestFinder {

    func doSomething<T>(_ function: (T) -> Void) -> Void {
        print("one")
    }

}

Это странная часть. Мы по-прежнему компилируем, хотя и говорим:

    func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
    TestFinder().doSomething(f)

Очевидно, что эта функция с четырьмя параметрами рассматривается компилятором как «подходящая» к объявлению только один generi c параметр.

Считаю это ошибкой. Думаю, я могу догадаться, что могло его вызвать; это могло быть связано с унаследованным списком параметров функции в виде кортежей. Эта функция f «эквивалентна» функции, принимающей одиночный параметр, состоящий из четырехстрочного кортежа. Тем не менее, вы не можете вызвать function внутри doSomething с четырехстрочным кортежем; Я не могу найти способ назвать это вообще. 1059 *.


ОБНОВЛЕНИЕ: По совету команды Swift я протестировал 4 мая 2020 года с набором инструментов Swift 5.3 Development. С его помощью ваш код компилируется и ведет себя так, как ожидалось. Это действительно была ошибка, и она была исправлена ​​как часть

https://bugs.swift.org/browse/SR-8563

Возвращаясь на мгновение к моей версии, мой код тоже компилируется и ведет себя как и ожидалось, присутствуют все четыре версии doSomething. Однако обратите внимание, что если вы удалите все, кроме первой версии doSomething, он все равно компилируется и запускается. Более того, вы можете вызвать function с четырьмя параметрами, объединив их в кортеж и принудительно применив приведение, например:

struct TestFinder2 {

    func doSomething<T>(_ function: (T) -> Void) -> Void {
        print("one")
        function(("manny", "moe", "jack", "henry") as! T)
    }

}

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

0 голосов
/ 07 мая 2020

Ключевым моментом является структура UserDetails, потому что эта структура имеет два свойства и без какого-либо разработанного инициализатора инициализатором может быть UserDetails (name:, roll:) или UserDetails (name:) или UserDetails ( roll :), это амбициозная часть. если вы просто удалите одно свойство UserDetails, это тоже будет работать, потому что одна структура свойств имеет только один разработанный инициализатор.

Если вы прокомментируете

func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U

Это означает, что искатель выбрал только один:

func getSomething<T,U,V>(_ function: @escaping(T,U) -> V) throws -> V
...