Ограничить связанный тип

Упрощенная версия моего кода, которая показывает проблему:

protocol Transformer {
    typealias Input
    typealias Output

    func transform(s: Input) -> Output
}

protocol InputType {}
protocol OutputType {}

extension Int: OutputType {}
extension String: InputType {}

struct StringToInt: Transformer {
    typealias Input = String
    typealias Output = Int

    func transform(s: Input) -> Output {
        return s.characters.count
    }
}

typealias Transform = InputType -> OutputType

func convert<T: Transformer where T.Input == InputType, T.Output == OutputType>
    (transformer: T) -> Transform {
    return transformer.transform
}

convert(StringToInt()) // error: Cannot invoke 'convert' with an argument list of type '(StringToInt)'

Я предполагаю, что ошибка происходит, потому что компилятор не может добраться до StringToInt и убедитесь, что Input а также Output действительно соответствуют InputType а также OutputType соответственно.

Для меня лучший способ решить эту проблему - ограничить связанные типы непосредственно в протоколе. Это было бы более выразительно, и у компилятора было бы больше информации. Но просто делаю typealias Input: InputType не работает

Есть ли способ ограничить связанный тип?

2 ответа

Решение

Вы можете создать расширение для Transformer протокол

extension Transformer where Input: InputType, Output: OutputType {
    func convert() -> Input -> Output {
        return transform
    }
}

Теперь вы можете позвонить convert метод на StringToInt пример.

StringToInt().convert()

Но для другого типа, который не имеет Input а также Output принятие InputType а также OutputType это не скомпилируется

struct DoubleToFloat: Transformer {
    func transform(s: Double) -> Float {
        return Float(s)
    }
}

DoubleToFloat().convert() // compiler error

Избавиться от Transform использование псевдонима, добавьте обобщенные значения для типов ввода и вывода и сопоставьте их с Transformerи вы получите рабочий код для StringToInt и для других трансформаторов, которые вы пишете:

func convert<T: Transformer, I, O where T.Input == I, T.Output == O>
    (transformer: T) ->  I -> O {
        return transformer.transform
}

convert(StringToInt())

Кстати, вам не нужно указывать псевдонимы типа для StringToIntкомпилятор может вывести их из определения функции, если вы укажете фактические типы:

struct StringToInt: Transformer {

    func transform(s: String) -> Int {
        return s.characters.count
    }
}
Другие вопросы по тегам