Protocols with associated protocols?

Would it be possible to have a protocol with associated protocols? For instance:

protocol Store {

    associatedprotocol RecordCodable

    func save(item: RecordCodable)

}

protocol SyncEngine: Store {

    associatedtype LocalStore: Store
    associatedtype RemoteStore: Store

    typealias RecordCodable = LocalStore.RecordCodable & RemoteStore.RecordCodable

    var localStore: LocalStore { get }
    var remoteStore: RemoteStore { get }

}

extension SyncEngine {

    func save(item: RecordCodable) {
        localStore.save(item: item)
        remoteStore.save(item: item)
    }

}
2 Likes

There was a thread about this a while ago:

I'd love to see this happen.

2 Likes

Generalized supertypes are great, but as the author alludes, the example provided still wouldn't be type-safe without some mechanism that would allow you to define an associated type or generic parameter that must be a protocol type (or of some other nature), i.e.

protocol P {
  protocol associatedtype Foo
}

func foo<protocol T>(_ arg: T) { ... }

This request is a little bit different than generalized supertype constraints as it introduces the ability to abstract over protocols. Generalized supertype constraints do not let you abstract over anything new. I would really love to see them happen and hope @Douglas_Gregor finds a way to sneak them into Swift 5 :crossed_fingers:. I have bumped into this limitation in the generic system very often.

A generalized supertype constraint is just a new way of constraining an associated type or generic parameter. This constraint requires an argument to be a subtype of the type bound that is bound to the constraining type variable (associated type or other generic parameter).

The associatedprotocol pitch is a subset of something more general: an associatedconstraint which would need to be paired with constraintalias. This is an extremely powerful feature I would really love to see someday. It is related to the Constraint Kinds extension in Haskell.

I have thought about starting a pitch thread but it seems wildly out of scope at the moment so I haven't yet. @Joe_Groff would a discussion on this topic be useful right now?

2 Likes

Any update on this?

I'm thinking about how to abstract over graphs with different kinds of nodes. Not even sure if thinking right about it but could this once work with associated protocols?

// `SelfProtocol`? Since `Self` in this context refers to the adopter type...
protocol Node {    
    var parent: (any SelfProtocol)? { get }
    var children: [any SelfProtocol] { get }
}


protocol FieldNode: Node {}

final class TextField: FieldNode {
    var parent: (any FieldNode)?
    var children: [any FieldNode]
}

final class PickerField: FieldNode {
    var parent: (any FieldNode)?
    var children: [any FieldNode]
}


protocol SourceNode: Node {}

final class File: SourceNode {
    var parent: (any SourceNode)?
    var children: [any SourceNode]
}

final class Link: SourceNode {
    var parent: (any SourceNode)?
    var children: [any SourceNode]
}

This would allow to build and work with any graphs of "Node" shape but separated "Field" and "Source" kinds. Without abstract classes, or other workarounds or boilerplates.