<html><head><style>
body {
        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
        padding:1em;
        margin:auto;
        background:#fefefe;
}
h1, h2, h3, h4, h5, h6 {
        font-weight: bold;
}
h1 {
        color: #000000;
        font-size: 28pt;
}
h2 {
        border-bottom: 1px solid #CCCCCC;
        color: #000000;
        font-size: 24px;
}
h3 {
        font-size: 18px;
}
h4 {
        font-size: 16px;
}
h5 {
        font-size: 14px;
}
h6 {
        color: #777777;
        background-color: inherit;
        font-size: 14px;
}
hr {
        height: 0.2em;
        border: 0;
        color: #CCCCCC;
        background-color: #CCCCCC;
display: inherit;
}
p, blockquote, ul, ol, dl, li, table, pre {
        margin: 15px 0;
}
a, a:visited {
        color: #4183C4;
        background-color: inherit;
        text-decoration: none;
}
#message {
        border-radius: 6px;
        border: 1px solid #ccc;
        display:block;
        width:100%;
        height:60px;
        margin:6px 0px;
}
button, #ws {
        font-size: 12 pt;
        padding: 4px 6px;
        border-radius: 5px;
        border: 1px solid #bbb;
        background-color: #eee;
}
code, pre, #ws, #message {
        font-family: Monaco;
        font-size: 10pt;
        border-radius: 3px;
        background-color: #F8F8F8;
        color: inherit;
}
code {
        border: 1px solid #EAEAEA;
        margin: 0 2px;
        padding: 0 5px;
}
pre {
        border: 1px solid #CCCCCC;
        overflow: auto;
        padding: 4px 8px;
}
pre > code {
        border: 0;
        margin: 0;
        padding: 0;
}
#ws { background-color: #f8f8f8; }
.bloop_markdown table {
border-collapse: collapse;
font-family: Helvetica, arial, freesans, clean, sans-serif;
color: rgb(51, 51, 51);
font-size: 15px; line-height: 25px;
padding: 0; }
.bloop_markdown table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0; }
.bloop_markdown table tr:nth-child(2n) {
background-color: #f8f8f8; }
.bloop_markdown table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px; }
.bloop_markdown table tr th :first-child, table tr td :first-child {
margin-top: 0; }
.bloop_markdown table tr th :last-child, table tr td :last-child {
margin-bottom: 0; }
.bloop_markdown blockquote{
border-left: 4px solid #dddddd;
padding: 0 15px;
color: #777777; }
blockquote > :first-child {
margin-top: 0; }
blockquote > :last-child {
margin-bottom: 0; }
code, pre, #ws, #message {
word-break: normal;
word-wrap: normal;
}
hr {
display: inherit;
}
.bloop_markdown :first-child {
-webkit-margin-before: 0;
}
code, pre, #ws, #message {
font-family: Menlo, Consolas, Liberation Mono, Courier, monospace;
}
.send { color:#77bb77; }
.server { color:#7799bb; }
.error { color:#AA0000; }</style></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class="bloop_markdown"><blockquote>
<p>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.</p>
</blockquote>
<p>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.</p>
<p>My first though was looking like this:</p>
<pre><code class="swift">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() {}
}
</code></pre>
<ul>
<li>Next step would be to import the module and extend <code>B</code> with <code>A</code> again, because it isn’t visible for the imported module. And that would lead to a huge problem.</li>
</ul>
<p><strong>BUT</strong> 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 <code>B</code> and moved to its own <code>extension</code> bag. But because the extended type <code>B</code> is <code>public</code> the compiler must raise an error (Fix-me?) for this particular <code>extension</code> that it cannot be <code>internal</code> when its members are coming from a <code>public protocol</code> and must retain <code>public</code> (iff the extend type is <code>public</code> as well).</p>
<p>The example from above will become this:</p>
<pre><code class="swift">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() {}
}
</code></pre>
<p>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.</p>
<p>In Swift we cannot suppress conformance visibility to lower visibility if the extended type is of higher or equal visibility as the protocol.</p>
<pre><code class="swift">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`
</code></pre>
<p><code>public > internal > fileprivate >= private</code> (Iff <code>private</code> is allowed at file scope.)</p>
<p>However we can grant visibility to members and still hide the conformance itself.</p>
<pre><code class="swift">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() {}
}
</code></pre>
<p>That said we could do the same with extensions as well.</p>
<pre><code class="swift">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() {}
}
</code></pre>
<p>This whole idea is to gain more consistent control of visibility and sort out the strange access control rules extensions currently have.</p>
<p>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:</p>
<pre><code class="swift">internal protocol A {}
public struct B {
public struct C {}
/* implicitly internal */ extension C : A {}
}
// Nested extension would remove this
internal extension B.C : A {}
</code></pre>
<p>One other thing I’d like to mention is this from the imported stdlib:</p>
<pre><code class="swift">extension ErrorProtocol {
}
</code></pre>
<p>How on earth is this possible?</p>
<pre><code class="swift">public protocol A {}
public extension A {
internal func foo()
}
</code></pre>
<p>The imported module would look like this, and there is no empty extension:</p>
<pre><code class="swift">public protocol A {}
</code></pre>
<p>Lets examine the impact on <strong>default protocol implementations</strong>:</p>
<p>Currently we have this behavior:</p>
<pre><code class="swift">public protocol A {
func foo()
}
extension A {
func foo() { /* implement */ }
}
</code></pre>
<p>The imported version would look like this.</p>
<pre><code class="swift">public protocol A {
public func foo()
}
</code></pre>
<p>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 <code>A</code> you don’t have to implement foo. This implicitly signals you that there is indeed a default implemenation</p>
<pre><code class="swift">struct B : A {} // This will be enough
A().foo() // this is fine
</code></pre>
<p>One could signal the module user that there is a default implementation by making the extension explicit public as well.</p>
<pre><code class="swift">// explicitly marked as public to grant visibility to
// the default implementation extension bag
public extension A {
/// will do something cool
func foo() { /* implement */ }
}
</code></pre>
<p>The result of the imported module would change and look like this:</p>
<pre><code class="swift">public protocol A {
public func foo()
}
extension A {
/// will do something cool
public func foo()
}
</code></pre>
<p>With the proposed change all default implementations will become visible by default and I think this is great step as well.</p>
<p>This will also allow us to hide default implementation for the public usage but still use it internally, which is kinda cool.</p>
<blockquote>
<p>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.</p>
</blockquote>
<p>You had me at this point, I misunderstood the behavior completely. I’ll have to rewrite the proposal to the mentioned behavior from above.</p>
<p>Yet I have no idea how complicated this change might be to implement, but I feel like this is a reasonable change.</p>
<p></p></div><div class="bloop_original_html"><style>body{font-family:Helvetica,Arial;font-size:13px}</style><div id="bloop_customfont" style="font-family:Helvetica,Arial;font-size:13px; color: rgba(0,0,0,1.0); margin: 0px; line-height: auto;"><br></div> <br> <div id="bloop_sign_1467008781638646016" class="bloop_sign"><div style="font-family:helvetica,arial;font-size:13px">-- <br>Adrian Zubarev<br>Sent with Airmail</div></div> <br><p class="airmail_on">Am 27. Juni 2016 um 02:41:28, Jordan Rose (<a href="mailto:jordan_rose@apple.com">jordan_rose@apple.com</a>) schrieb:</p> <blockquote type="cite" class="clean_bq"><span><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div></div><div>
<title></title>
<div class="">Hi, Adrian. Can you explain <i class="">why</i> 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.</div>
<div class=""><br class=""></div>
<div class="">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.</div>
<div class=""><br class=""></div>
<div class="">Jordan</div>
</div></div></span></blockquote></div><div class="bloop_markdown"><p></p></div></body></html>