Swift protocol extension implementing another protocol with shared associated type
Asked Answered
W

2

6

Consider the following:

protocol Foo {
  typealias A
  func hello() -> A
}
protocol FooBar: Foo {
  func hi() -> A
}
extension FooBar {
  func hello() -> A {
    return hi()
  }
}

class FooBarClass: FooBar {
  typealias A = String
  func hi() -> String {
    return "hello world"
  }
}

This code compiles. But if I comment out explicit definition of associated type typealias A = String, then for some reason, swiftc fails to infer the type.

I'm sensing this has to do with two protocols sharing the same associated type but without a direct assertion through, for example, type parameterization (maybe associated type is not powerful/mature enough?), which makes it ambiguous for type inference.

I'm not sure if this is a bug / immaturity of the language, or maybe, I'm missing some nuances in protocol extension which rightfully lead to this behaviour.

Can someone shed some light on this?

Wisner answered 28/11, 2015 at 5:35 Comment(4)
when you say swift fails to infer the type, what is the compiler error and on which line?Benzo
@PatrickGoley Swift fails to see hello() and hi()'s returning type A to be of equivalent type and thusly, require me to implement hello() method additionally. It says FooBarClass fails to conform to protocol FooBarWisner
I think this should be considered as a bug. Workaround I found is declaring typealias in FooBar protocol. ie: protocol FooBar: Foo { typealias A; func hi() -> A }Nonoccurrence
@Nonoccurrence sorry, i didn't see you note before i send my answer.Blender
B
1

look at this example

protocol Foo {
    typealias A
    func hello() -> A
}
protocol FooBar: Foo {
    typealias B
    func hi() -> B
}
extension FooBar {
    func hello() -> B {
        return hi()
    }
}

class FooBarClass: FooBar {
    //typealias A = String
    func hi() -> String {
        return "hello world"
    }
}

with generics

class FooBarClass<T>: FooBar {
    var t: T?
    func hi() -> T? {
        return t
    }
}

let fbc: FooBarClass<Int> = FooBarClass()
fbc.t = 10
fbc.hello() // 10
fbc.hi()    // 10
Blender answered 28/11, 2015 at 19:37 Comment(1)
It worth mentioning that this is a bug in Swift compiler, and that B can be named the same as A.Bluestone
B
0

Providing explicit values for associated types in a protocol is required for conformance to said protocol. This can be accomplished by hard coding a type, as you've done with typealias A = String, or using a parameterized type as you mentioned, such as below:

class FooBarClass<T>: FooBar {
    typealias A = T
    ...
}

Swift will not infer your associated type from an implemented method of the protocol, as there could be ambiguity with multiple methods with mismatching types. This is why the typealias must be explicitly resolved in your implementing class.

Benzo answered 28/11, 2015 at 6:37 Comment(1)
Could you elaborate more on "as there could be ambiguity with multiple methods with mismatching types."?Wisner

© 2022 - 2024 — McMap. All rights reserved.