I’m trying to mix generics with protocols and I’m getting a really hard time xD
I have certain architecture implemented in an Android/Java project, and I’m trying to rewrite it to fit it in a swift/iOS project. But I’ve found this limitation.
ProtocolA
protocol ProtocolA { }
ProtocolB
protocol ProtocolB : ProtocolA { }
ImplementProtocolA
class ImplementProtocolA <P : ProtocolA> { let currentProtocol : P init(currentProtocol : P) { self.currentProtocol = currentProtocol } }
ImplementProtocolB
class ImplementProtocolB : ImplementProtocolA<ProtocolB> { }
So, when I try to set ProtocolB as the concrete type that implements ProtocolA, I get this error:
Using 'ProtocolB' as a concrete type conforming to protocol 'ProtocolA' is not supported
1 Is there any reason for this “limitation”?
2 Is there any workaround to get this implemented?
3 Will it be supported at some point?
--UPDATED--
Another variant of the same problem, I think:
View protocols
protocol View { } protocol GetUserView : View { func showProgress() func hideProgress() func showError(message:String) func showUser(userDemo:UserDemo) }
Presenter protocols
protocol Presenter { typealias V : View } class UserDemoPresenter : Presenter { typealias V = GetUserView }
Error:
UserDemoPresenter.swift Possibly intended match 'V' (aka 'GetUserView') does not conform to 'View’
What is that?? It conforms!
Even if I use View instead of GetUserView, it does not compile.
class UserDemoPresenter : Presenter { typealias V = View }
UserDemoPresenter.swift Possibly intended match 'V' (aka 'View') does not conform to 'View'
xxDD I don’t get it, really.
--UPDATED--
With the solution proposed by Rob Napier the problem is not fixed, instead, it is just delayed.
When a try to define a reference to UserDemoPresenter, I need to specify the generic type, so I get the same error:
private var presenter : UserDemoPresenter<GetUserView>
Using 'GetUserView' as a concrete type conforming to protocol 'GetUserView' is not supported
One protocol can inherit from another in a process known as protocol inheritance. Unlike with classes, you can inherit from multiple protocols at the same time before you add your own customizations on top. Now we can make new types conform to that single protocol rather than each of the three individual ones.
Protocol composition is the process of combining multiple protocols into a single protocol. You can think of it as multiple inheritance. With protocol DoSomething , below, class HasSomethingToDo is able to do whatShouldBeDone and anotherThingToBeDone .
The underlying reason for the limitation is that Swift doesn't have first-class metatypes. The simplest example is that this doesn't work:
func isEmpty(xs: Array) -> Bool { return xs.count == 0 }
In theory, this code could work, and if it did there would be a lot of other types I could make (like Functor and Monad, which really can't be expressed in Swift today). But you can't. You need to help Swift nail this down to a concrete type. Often we do that with generics:
func isEmpty<T>(xs: [T]) -> Bool { return xs.count == 0 }
Notice that T
is totally redundant here. There is no reason I should have to express it; it's never used. But Swift requires it so it can turn the abstract Array
into the concrete [T]
. The same is true in your case.
This is a concrete type (well, it's an abstract type that will be turned into a concrete type any time it's instantiated and P
is filled in):
class ImplementProtocolA<P : ProtocolA>
This is a fully abstract type that Swift doesn't have any rule to turn into a concrete type:
class ImplementProtocolB : ImplementProtocolA<ProtocolB>
You need to make it concrete. This will compile:
class ImplementProtocolB<T: ProtocolB> : ImplementProtocolA<T> {}
And also:
class UserDemoPresenter<T: GetUserView> : Presenter { typealias V = T }
Just because you're likely to run into the issue later: your life will go much easier if you'll make these structs or final
classes. Mixing protocols, generics, and class polymorphism is full of very sharp edges. Sometimes you're lucky and it just won't compile. Sometimes it will call things you don't expect.
You may be interested in A Little Respect for AnySequence which details some related issues.
private var presenter : UserDemoPresenter<GetUserView>
This is still an abstract type. You mean:
final class Something<T: GetUserView> { private var presenter: UserDemoPresenter<T> }
If that creates a problem, you'll need to create a box. See Protocol doesn't conform to itself? for discussion of how you type-erase so that you can hold abstract types. But you need to work in concrete types. You can't ultimately specialize on a protocol. You must eventually specialize on something concrete in the majority of cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With