2016-01-10 2 views
0

упрощенная версия моего кода, который показывает проблему:Ограничить ассоциированный тип

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 не работает.

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

ответ

1

Вы можете создать расширение для 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 
+0

Но если заменить возвращение тип 'convert()' будет 'InputType -> OutputType' вместо' Input -> Output', он больше не работает. Зачем? – mschumacher

+0

Потому что 'transform' is' Input -> Output' не 'InputType -> OutputType'. Вы не можете обрабатывать протоколы как обычные типы в Swift большую часть времени. Однако это не является причиной ошибки здесь. Посмотрите на это изображение для деталей. http://milen.me/images/writings/swift-generic-protocols/swift-protocols-two-worlds.png – mustafa

0

Избавиться от использования в 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 
    } 
} 
Смежные вопросы