Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using some protocol as a concrete type conforming to another protocol is not supported

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

like image 320
Víctor Albertos Avatar asked Nov 03 '15 15:11

Víctor Albertos


People also ask

Can a protocol conform to another protocol?

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.

What is protocol composition and protocol extension?

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 .


1 Answers

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.

like image 128
Rob Napier Avatar answered Oct 20 '22 05:10

Rob Napier