How to workaroud missing `AnyProtocol`?

Here is a very simple example. I have concrete Container types which conform to a lot of Container protocols. Then there is a distinction between two kinds of containers called Signal the other Driver. Now I want to create a generic API wrapper that provides access to a specific API for one of the kinds depending on the constraint from the generic parameter list. The issue is that currently it's not supported to use the concrete protocol from the generic parameter list as a constraint on inner generic methods.

struct Converter<Constraint> /* where Constraint : AnyProtocol */ {
   // I have multiple methods like this one
   func generic<Container>(
     _ path: KeyPath<SomeStart, Container>
   ) -> NotificationSequence<Container>
     where Container : Constraint /* here is what I'd like to do */ {
     ...
   }
}

// then I could create multiple constraints and reuse the API instead copying it over and over.
var signal: Converter<SignalConstraint> ... 
var driver: Converter<DriverConstraint> ...

Any idea for an alternative design?


Other then the problem I'm trying to solve in my code, would this be a good motivation to propose a general AnyProtocol keyword?

This looks related to what I have called "generalized supertype constraints". The difference is that a generalized supertype constraint would accept any subtyping relationship rather than requiring Constraint to be a protocol.

This is still pretty close to what your request would enable as your design is already restricted to protocols that can be used as types anyway. What does the extra AnyProtocol constraint buy you? Is there a reason your design needs to exclude other subtyping relationships?

In case you're interested, I have a PR to add this to the generics manifesto that has been open since November: Add generic associatedtypes and generalized supertype constraints by anandabits · Pull Request #13012 · apple/swift · GitHub. I would really like to see this added soon as this limitation in the generics system is one I have bumped into many times.

2 Likes

Well that's completely true that if generalized supertype constraints existed I wouldn't really need any AnyProtocol. I remembered your thread when I saw you typing. :slight_smile: I'm dying to see both features from your gist. My project is heavily relying a quite a lot of generic features at compile time, which makes my API strongly constrained, safe and at the same time super convenient for the end-user to use.

Especially the trick I wanted to achieve to reuse the same API surface with different constraints would decrease the amount of work and maintenance in my project.

@anandabits here is a generic type I'd like to build which requires AnyProtocol.

final class DelegateProxy<Delegate, Receiver> 
  where  
  Delegate : AnyProtocol /* enables `weak` */, 
  Receiver : Delegate /* makes use of generalized supertype constraints */ {
  weak var delegate: Delegate?
  weak var receiver: Receiver?
}

AnyProtocol would not enable weak unless it was a protocol that refined AnyObject which is a constraint you don't include here.

What would you like to be able to do with DelegateProxy? The name gives some suggestion but it isn't immediately clear what problem you are trying to solve with this type.

I want to build multiple proxy types that have the same logic and I don't want to repeat the same code over and over again. For that case I need the help of Swifts generics but at the moment there is no way to constrain the generic parameter type in a way to allow the properties to be weak without AnyObject. Since protocols do not conform to them-selfs I cannot pass a concrete protocol to the generic proxy type, even though we allow weak class constrained protocols.

Actually you're right again, thanks for pointing this out. The constraint I need is a different one.

Can you provide a more complete sketch of what you are trying to do?

Sure here you go. That's what I ended up building instead because I cannot create the constraint I need in the current Swift.

As you may notice the implementation is the same except for the types and for an extra extension which for a generic type would be conditional.


I rather would want to have a generic type and simply path the required types into the generic parameter list than implementing the same thing twice.

let proxy = DelegateProxy<CBPeripheralDelegate, PeripheralProxyReceiver>(...)

More info: I need such a type because two different libraries re-assign the delegate and one lib always looses the events. The proxy is injected into von lib to fix that issue under the assumption on how the other lib assigns the delegates.