Hi, Adrian. Can you explain why you want to make this change? “public” on an
extension doesn’t mean anything by itself because you can’t refer to an
extension as an entity in and of itself. Access modifiers are disallowed on
extensions with protocols because the conformance isn’t controlled by the
access modifier and we didn’t want to give the impression that it would.
Now that you mention this, I did again some tests and I think I now understand
why controlling conformance with access modifier would be fatal, but I think
this edge-case also can be banned easily.
My first though was looking like this:
public protocol A { func foo() }
public struct B {}
// If we had the same access control like on structs/ classes
// We could suppress the visibility (imagine: `internal struct B : A`)
internal extension B : A {
/* implicitly internal */ func foo(){}
// Some other custom members for this extension bag
func boo() {}
}
Next step would be to import the module and extend B with A again, because it
isn’t visible for the imported module. And that would lead to a huge problem.
BUT this is a very wrong thought, because if extensions would have the same
access control as other types, this edge case cannot happen at all. The
conformance itself is applied on the type B and moved to its own extension bag.
But because the extended type B is public the compiler must raise an error
(Fix-me?) for this particular extension that it cannot be internal when its
members are coming from a public protocol and must retain public (iff the
extend type is public as well).
The example from above will become this:
public protocol A { func foo() }
public struct B {}
public extension B : A {
public func foo(){}
// Access modifier on members won't be overridden by the extension access
modifier anymore
// And they will respect the access level boundary set by the extension
func boo() {}
}
To sum this example up: the access modifier on extensions should only have
control of its bag visibility in respect to the access level of the extended
type. This would be consistent to structs, enums and classes.
In Swift we cannot suppress conformance visibility to lower visibility if the
extended type is of higher or equal visibility as the protocol.
public protocol A { func foo() }
public struct B : A {
// foo must retain public
public func foo() {}
}
internal protocol C : A {
// foo must retain internal
// we cannot grant foo more visibility than the type its implemented in
/* implicitly internal */ func foo() {}
}
// same for `fileprivate` and `private`
public > internal > fileprivate >= private (Iff private is allowed at file
scope.)
However we can grant visibility to members and still hide the conformance
itself.
internal protocol A { func foo() }
public protocol B : A {
// foo won't be visible when imported
func foo() {}
}
public protocol C : A {
// we can grant foo more visibility than it originally had
// foo will be visible when imported, but the
// conformance to `A` will not be visible
public func foo() {}
}
That said we could do the same with extensions as well.
internal protocol A { func foo() }
public struct B {}
public extension B : A {
// we can grant foo visibility but still hide conformance to A
// and move everything from `A` to an extra extension bag
public func foo(){}
// Access modifier on members won't be overridden by the extension access
modifier anymore
// And they will respect the access level boundary set by the extension
func boo() {}
}
This whole idea is to gain more consistent control of visibility and sort out
the strange access control rules extensions currently have.
If I’m correct this also would allow us to nest extension (but this needs an
other thread in the future) if there is any desire to do so:
internal protocol A {}
public struct B {
public struct C {}
/* implicitly internal */ extension C : A {}
}
// Nested extension would remove this
internal extension B.C : A {}
One other thing I’d like to mention is this from the imported stdlib:
extension ErrorProtocol {
}
How on earth is this possible?
public protocol A {}
public extension A {
internal func foo()
}
The imported module would look like this, and there is no empty extension:
public protocol A {}
Lets examine the impact on default protocol implementations:
Currently we have this behavior:
public protocol A {
func foo()
}
extension A {
func foo() { /* implement */ }
}
The imported version would look like this.
public protocol A {
public func foo()
}
As the module user you have no clue that there might be a default
implementation, but you sill will be able to use it, because when conforming to
A you don’t have to implement foo. This implicitly signals you that there is
indeed a default implemenation
struct B : A {} // This will be enough
A().foo() // this is fine
One could signal the module user that there is a default implementation by
making the extension explicit public as well.
// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
/// will do something cool
func foo() { /* implement */ }
}
The result of the imported module would change and look like this:
public protocol A {
public func foo()
}
extension A {
/// will do something cool
public func foo()
}
With the proposed change all default implementations will become visible by
default and I think this is great step as well.
This will also allow us to hide default implementation for the public usage but
still use it internally, which is kinda cool.
There’s really no such thing as an “implicitly public extension”. An extension
is just a bag of additional members and conformances. An access modifier on the
extension sets the default access level of members in the extension as a
convenience.
You had me at this point, I misunderstood the behavior completely. I’ll have to
rewrite the proposal to the mentioned behavior from above.
Yet I have no idea how complicated this change might be to implement, but I
feel like this is a reasonable change.
--
Adrian Zubarev
Sent with Airmail
Am 27. Juni 2016 um 02:41:28, Jordan Rose ([email protected]) schrieb:
Hi, Adrian. Can you explain why you want to make this change? “public” on an
extension doesn’t mean anything by itself because you can’t refer to an
extension as an entity in and of itself. Access modifiers are disallowed on
extensions with protocols because the conformance isn’t controlled by the
access modifier and we didn’t want to give the impression that it would.
There’s really no such thing as an “implicitly public extension”. An extension
is just a bag of additional members and conformances. An access modifier on the
extension sets the default access level of members in the extension as a
convenience.
Jordan
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution