swiftcombine

"some Protocol" causes type to not conform to protocol


I don't understand why this doesn't compile. It does if I remove the where restriction from the P type.

import Combine

protocol Foo {
    associatedtype P: Publisher where P.Output == Int
    var publisher: P { get }
}

struct Bar: Foo {
    var publisher: some Publisher {
        Just(1)
    }
}

The error says that Type 'Bar' does not conform to protocol 'Foo'. I guess it's because publisher return type is not just any some Publisher. But in SwiftUI, the View uses a similar approach, just that it doesn't have restrictions over the View type.

Is there any way I can make this code to compile?


Solution

  • The reason why it doesn't compile is because some Publisher declares an opaque type, but the protocol requires that the type must be "see-through".

    some Publisher is "opaque" in the sense that callers cannot see exactly what type the property actually is, and can only know that it conforms to Publisher. This directly contradicts with the protocol requirement that P.Output has to be Int. To check P.Output is Int, you have to "see through" some Publisher, but you can't.

    Since the compiler can't check the publisher's Output, it can't check whether your type really conforms to the protocol or not. Therefore it chooses the "safe route" concludes that your type does not conform to the protocol.

    I think you should use the AnyPublisher type eraser:

    var publisher: AnyPublisher<Int, Never> {
        Just(1).eraseToAnyPublisher()
    }
    

    With the primary associated type feature introduced in Swift 5.7, Output and Error are now the primary associated types of Publisher. This allows you to write

    var publisher: some Publisher<Int, Never> {
        Just(1)
    }
    

    SwiftUI's View protocol does not have this problem because it does not require Body to be "see-through". It just requires that Body is a conformer of View, which some View, by definition, is.