Swift doesn’t support protocols with greater visibility than their requirements. So what is the best technique available to emulate it?
Here, there are two types, S1 and S2, which are generic over types that conform to a protocol. Everything about that protocol should actually be private, except for its name. All of the types that conform to it are known (E1 and E2, here), and I want to extend each of them, privately, to conform to the protocol that the type within the file is generic over. *
The best I’ve come up with is to raise the visibility of the “private“ requirements, but gate them behind associated types.
If the purpose is “let people know they’re not supposed to use this”, I would just put an underscore before the requirement name. If the purpose is “actually keep other people from calling this”, you can get away with it by having every requirement take an extra argument whose type only has an internal initializer, so nobody outside your module can actually do it without resorting to shenanigans. If it’s “keep other people from implementing this”, have a requirement that returns such a type.
But yeah, I wish you could have private requirements too. Someone would need to propose it and implement it properly though.
That all sounds good too, but as you can see above, the main problem is the namespace pollution that happens when you add requirements to a non-private type in multiple files. I.e. the E enums will each have two competing definitions for property without the associatedtype namespaces.
Ah, that was not obvious to me, it isn’t in your description at all! Then, yeah, every answer I have is some variation of prefixing or wrapping. :-/
I am somewhat curious what the protocols and concrete types are in your real example, though, because it feels unusual to have two parallel protocols that are basically identical, and also have types that conform to both. Swift not having true generic protocols means this could just be compensating for the code you’d actually like to write, but maybe not?
A type cannot conform to a protocol in more than one way. So, what is an alternative to this delegating to static members, when you want differing behavior based solely on generic type parameters?
I think in terms of a potential evolution proposal, there are two kinds of "I want private protocol requirements":
"I want to ensure that the complete set of implementors is known" might be expressed as @sealed public protocol P { ... }; the compiler would then refuse to create a conformance to P outside the declaring module. An example use-case might be a database library where the set of primitive column types is fixed.
"I want to have a requirement that can't be called from outside this module, and external conformances must use the default implementation" might be expressed as public protocol P { internal func f() }; extension P { func f() { ... } }. This is "the SwiftUI problem" (where external conformances should provide body, but primitive views need to implement the private _makeViewList function).
Although you can potentially implement 1. using 2. (by not providing a default implementation for an internal requirement), I feel like these are really orthogonal ideas?