Pitch: Protocols with private fields

Exactly. Only if adopting a protocol with scope means also to override the scope for requirements, then scoped conformances can solve this.

Actually I'll side with @dabrahams and @xwu on that matter. Their example is indeed one way to implement my use case. Although QImpl has to be exposed, crucially, the implementation details brought by QImpl are not exposed by the conforming type.

The approach proposed in this pitch is to completely encapsulate an implementation from the outside. From the perspective of a type consumer, though, that's a distinction without a difference. Further, as suggested by @dabrahams, scoped conformance would allow a library to vend multiple implementations for the same protocol.

// In module A
protocol P {}
protocol PImplA: P { ... }
protocol PImplB: P { ... }

// In module B
struct S: P, private: PImplA { ... }
struct T: P, private: PImplB { ... }

Here, both B.S and B.T conform to A.P, but they use two different implementations defined in module A.

I disagree with @dabrahams on a couple of points though:

Scoped conformance have their own subtleties. For instance, dynamic dispatch resolution is a rather significant one. In particular, I am worried about that sentence:

The post ends with a handful of other difficult questions. Generally, it seems like dealing with multiple conformance and conformance inheritance will compel us to come up with new rules.

Although I don't claim such mechanisms cannot be designed or understood, having formalized and partially reimplemented Swift's current method resolution I'd argue that it's definitely not "simple".

My pitch does not challenge that assertion. A protocol would describe a single, well-understood API with the addition be that access levels would be part of that API.

While I adhere wholeheartedly to the principle of "economy of concept", I'd like to point out that the setup for implementing my use case with scoped conformance is more involved and feels a little like a "trick".

I'd be interested to discuss things the other way around. What use case solved by scoped conformance cannot be solved by access lower bounds?

1 Like

I see this as a very useful part of scoped conformances.

But regardless whether the implementation protocol definition is public, its interface is not exposed publicly as part of the type (in this example, U), fulfilling the stated design goal of not exposing implementation details in the type’s public interface. Indeed, if it were possible to declare a private member of a public protocol, the declaring protocol is public anyway.

I thought this pitch is about protocol requirements with lover access level than the protocol itself so a default implementation can access a fulfilment without that fulfilment being exposed. I'm having troubles to follow informal language sentences here. Can we express ideas to solve the original problem in Swift? For instance in the example from @xwu:

Do we know if this fileprivate foo fulfilment in U is legit in private conformance of internal QImpl's requirement? Will this compile? It seems to me that this will decide if scoped conformances solves what this pitch asks for.

1 Like

Allowing the protocol’s requirements not to be exposed publicly is precisely the definition of a scoped conformance. If this example does not compile (as it currently does not), then Swift does not support scoped conformances.

Whoops it looks like I missed these...

(where Wrapper was internal in a previous figure)

and


Initially, I understood that protocol requirements must be fulfilled with protocol's access level (like today), and the scoped conformance just limits visibility of that conformance. Which fortunately seems (based on quotes above) to be incorrect.

I only made it public to demonstrate its use across modules: a module can publish a helper protocol that encapsulates common patterns for implementing its other protocols.

Can scoped conformances deal with an EasyXImpl / QImpl protocol which is not public?

Certainly.

public protocol X { ... }
private protocol EasyXImpl {}

extension X where Self: EasyXImpl {
  // implementations of X requirements in terms of EasyXImpl requirements
}

public struct Y: X private EasyXImpl {
  // EasyXImpl requirements
}

What about that sentence made you worry? We were not wedded to that particular semantics, but it seemed logical enough to us.

The post ends with a handful of other difficult questions.

Well, I still disagree with that assertion; as the proposal says:

Generally, it seems like dealing with multiple conformance and conformance inheritance will compel us to come up with new rules.

I don't think it increases our obligation to come up with rules, because as noted in that thread we have all the same issues today in a slightly different form.

Although I don't claim such mechanisms cannot be designed or understood, having formalized and partially reimplemented Swift's current method resolution I'd argue that it's definitely not "simple".

Ah well, I never said requirement resolution was simple.

My pitch does not challenge that assertion. A protocol would describe a single, well-understood API with the addition be that access levels would be part of that API.

This depends on your point of view I guess. The way I see it, each access level exposes a different API.

That's an interesting question. Would these lower bounds solve Doug's problem or address the metadata and witness table bloat issues for which scoped conformances were originally designed? Another use case: two different modules want a conformance of public type Int to public protocol C.Monoid, but one module wants to use the (+, 0) monoid and the other wants to use the (*, 1) monoid.

I use Protocols and extensions to form part of a refactor, where you want to hide the actual protocol implementation from the outside world. I also use it to enforce implementation requirements on a class.

Ie, a certain parameter needs to exist in the class, but you want its access to the outside to be hidden.

The only way this can be done currently using using class inheritence. But it seems like this is not the way of the future for protocol oriented programming.

I believe that protocols are great for abstractions like lists, but not so useful in the real world when dealing with less abstract concepts like specific view/data transforms and implementations.