<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">All bike-shedding aside, I see two parts of this proposal and I come down on different sides for the two.</div><div class=""><br class=""></div><div class="">1. Introducing a new class modifier that acts as final outside the defining module</div><div class="">2. Changing the default to this new modifier (and thus also introducing a third explicit class modifier that represents the current default (non-final) behavior.</div><div class=""><br class=""></div><div class="">I think that the behavior of the new proposed modifier is useful. I imagine that it would allow for the same type of optimizations as a final class (assuming whole module optimizations) while remaining flexible within the module itself. We’re currently using a public protocol with one or more internal non-final classes to achieve similar results in the specific scenario where we want a unified external type. </div><div class=""><br class=""></div><div class="">On the topic of changing the default, I’m against for the same reasons that was brought up in the discussion of making final the default. Since those reasons were mostly about usage outside of the same module, I find them equally valid in this case.</div><div class=""><br class=""></div><div class="">- David</div><div class=""><br class=""></div><br class=""><div><blockquote type="cite" class=""><div class="">On 28 Jun 2016, at 00:40, Javier Soto via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">Hello!<div class=""><br class=""></div><div class="">I sent this as a <a href="https://github.com/apple/swift-evolution/pull/376" class="">PR</a> on the swift-evolution repo, but we never had any discussion about it on-list, besides <a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/9702/focus=9708" class="">a long time ago</a>. Here's the first draft of the proposal:</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><h1 style="font-size:2.25em;margin:0px 0px 16px;line-height:1.2;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol"" class="">Sealed classes by default</h1><h2 style="margin-top:1em;margin-bottom:16px;line-height:1.225;font-size:1.75em;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol"" class=""><a id="inbox-inbox-user-content-introduction" class="inbox-inbox-anchor" href="https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#introduction" style="color:rgb(64,120,192);text-decoration:none;display:inline-block;padding-right:2px;line-height:1;background-color:transparent"></a>Introduction</h2><p style="margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px" class="">Introduce a new<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">sealed</code><span class="inbox-inbox-Apple-converted-space"> </span>class modifier that makes classes and methods<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">final</code><span class="inbox-inbox-Apple-converted-space"> </span>outside of the module they're declared in, but non-<code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">final</code><span class="inbox-inbox-Apple-converted-space"> </span>within the module.</p><h2 style="margin-top:1em;margin-bottom:16px;line-height:1.225;font-size:1.75em;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol"" class=""><a id="inbox-inbox-user-content-motivation" class="inbox-inbox-anchor" href="https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#motivation" style="color:rgb(64,120,192);text-decoration:none;display:inline-block;padding-right:2px;line-height:1;background-color:transparent"></a>Motivation</h2><ul style="padding-left:2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px" class=""><li style="box-sizing: border-box;" class="">It is not uncommon to have a need for a reference type without needing inheritance. Classes must be intentionally designed to be subclassable, carefully deciding which methods are the override entry-points such that the the behavior remains correct and subclasses respect the<span class="inbox-inbox-Apple-converted-space"> </span><a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle" style="color:rgb(64,120,192);text-decoration:none;background-color:transparent" class="">Liskov substitution principle</a>.</li><li style="box-sizing: border-box;" class="">Defaulting to non-<code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">final</code><span class="inbox-inbox-Apple-converted-space"> </span>allows the author of a class to accidentally leave the visible methods open for overrides, even if they didn't carefully consider this possibility.</li><li style="box-sizing: border-box;" class="">Requiring that the author of a class mark a class as<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">open</code><span class="inbox-inbox-Apple-converted-space"> </span>is akin to requiring symbols to be explicitly<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">public</code>: it ensures that a conscious decision is made regarding whether the ability to subclass a<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">class</code><span class="inbox-inbox-Apple-converted-space"> </span>is part of the API.</li></ul><h2 style="margin-top:1em;margin-bottom:16px;line-height:1.225;font-size:1.75em;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol"" class=""><a id="inbox-inbox-user-content-proposed-solution" class="inbox-inbox-anchor" href="https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#proposed-solution" style="color:rgb(64,120,192);text-decoration:none;display:inline-block;padding-right:2px;line-height:1;background-color:transparent"></a>Proposed solution</h2><ul style="padding-left:2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px" class=""><li style="box-sizing: border-box;" class="">New<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">sealed</code><span class="inbox-inbox-Apple-converted-space"> </span><span style="line-height:25.6px" class="">(</span><em style="line-height:25.6px" class="">actual name pending bike-shedding</em><span style="line-height:25.6px" class="">) </span>class modifier for classes and methods which marks them as only overridable within the module they're declared in.</li><li style="box-sizing: border-box;" class=""><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">sealed</code><span class="inbox-inbox-Apple-converted-space"> </span>becomes the default for classes and methods.</li><li style="box-sizing: border-box;" class="">New<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">open</code><span class="inbox-inbox-Apple-converted-space"> </span>(<em style="box-sizing: border-box;" class="">actual name pending bike-shedding</em>) class modifier to explicitly mark a class or a method as<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">overridable</code>.</li></ul><h2 style="margin-top:1em;margin-bottom:16px;line-height:1.225;font-size:1.75em;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol"" class=""><a id="inbox-inbox-user-content-detailed-design" class="inbox-inbox-anchor" href="https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#detailed-design" style="color:rgb(64,120,192);text-decoration:none;display:inline-block;padding-right:2px;line-height:1;background-color:transparent"></a>Detailed design</h2><p style="margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px" class="">Code Examples:</p><div class="inbox-inbox-highlight inbox-inbox-highlight-source-swift" style="margin-bottom:16px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px"><pre style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;margin-top:0px;margin-bottom:0px;font-stretch:normal;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;border-radius:3px;word-break:normal;background-color:rgb(247,247,247)" class=""><span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// ModuleA:</span>
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This class is `sealed` by default.</span>
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This is equivalent to `sealed class SealedParentClass`</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">class</span> SealedParentClass {
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This method is `sealed` by default`.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">foo</span>()
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This raises a compilation error: a method can't have a "subclassability"</span>
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// level higher than that of its class.</span>
open <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">bar</span>()
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// The behavior of `final` methods remains unchanged.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">final</span> <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">baz</span>()
}
open <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">class</span> OpenParentClass {
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This method is `sealed` by default`.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">foo</span>()
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// Overridable methods in an `open` class must be explicitly marked as `open`.</span>
open <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">bar</span>()
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// The behavior of a `final` method remains unchanged.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">final</span> <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">baz</span>()
}
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// The behavior of `final` classes remains unchanged.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">final</span> <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">class</span> FinalClass { }</pre></div><div class="inbox-inbox-highlight inbox-inbox-highlight-source-swift" style="margin-bottom:16px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px"><pre style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;margin-top:0px;margin-bottom:0px;font-stretch:normal;line-height:1.45;word-wrap:normal;padding:16px;overflow:auto;border-radius:3px;word-break:normal;background-color:rgb(247,247,247)" class=""><span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// ModuleB:</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">import</span> <span class="inbox-inbox-pl-c1" style="color:rgb(0,134,179)">ModuleA</span>
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This raises a compilation error: ParentClass is effectively `final` from</span>
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// this module's point of view.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">class</span> SubclassA <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">:</span> SealedParentClass { }
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This is allowed since `OpenParentClass` has been marked explicitly `open`</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">class</span> SubclassB <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">:</span> OpenParentClass {
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This raises a compilation error: `OpenParentClass.foo` is</span>
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// effectively `final` outside of `ModuleA`.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">override</span> <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">foo</span>() { }
<span class="inbox-inbox-pl-c" style="color:rgb(150,152,150)">/// This is allowed since `OpenParentClass.bar` is explicitly `open`.</span>
<span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">override</span> <span class="inbox-inbox-pl-k" style="color:rgb(167,29,93)">func</span> <span class="inbox-inbox-pl-en" style="color:rgb(121,93,163)">bar</span>() { }
}</pre></div><h2 style="margin-top:1em;margin-bottom:16px;line-height:1.225;font-size:1.75em;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol"" class=""><a id="inbox-inbox-user-content-impact-on-existing-code" class="inbox-inbox-anchor" href="https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#impact-on-existing-code" style="color:rgb(64,120,192);text-decoration:none;display:inline-block;padding-right:2px;line-height:1;background-color:transparent"></a>Impact on existing code</h2><ul style="padding-left:2em;margin-top:0px;margin-bottom:16px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px" class=""><li style="box-sizing: border-box;" class="">This would be a backwards-breaking change for all classes and methods that are public and non-final, which code outside of their module has overriden. Those classes/methods would fail to compile. Their superclass would need to be changed to<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">open</code>.</li></ul><h2 style="margin-top:1em;margin-bottom:16px;line-height:1.225;font-size:1.75em;padding-bottom:0.3em;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgb(238,238,238);color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol"" class=""><a id="inbox-inbox-user-content-alternatives-considered" class="inbox-inbox-anchor" href="https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#alternatives-considered" style="color:rgb(64,120,192);text-decoration:none;display:inline-block;padding-right:2px;line-height:1;background-color:transparent"></a>Alternatives considered</h2><ul style="padding-left:2em;margin-top:0px;color:rgb(51,51,51);font-family:"helvetica neue",helvetica,"segoe ui",arial,freesans,sans-serif,"apple color emoji","segoe ui emoji","segoe ui symbol";font-size:16px;line-height:25.6px;margin-bottom:0px" class=""><li style="box-sizing: border-box;" class="">Defaulting to<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">final</code><span class="inbox-inbox-Apple-converted-space"> </span>instead: This would be comparable to Swift defaulting to<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">private</code>, as opposed to<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">internal</code>. Just like<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">internal</code><span class="inbox-inbox-Apple-converted-space"> </span>is a better trade-off,<span class="inbox-inbox-Apple-converted-space"> </span><code style="font-family:consolas,"liberation mono",menlo,courier,monospace;font-size:13.6px;padding:0.2em 0px;margin:0px;border-radius:3px;background-color:rgba(0,0,0,0.0392157)" class="">sealed</code><span class="inbox-inbox-Apple-converted-space"> </span>by default also makes sure that getting started with Swift, writing code within a module, doesn't require a lot of boilerplate, and fighting against the compiler.</li></ul></div></div><div dir="ltr" class="">-- <br class=""></div><div data-smartmail="gmail_signature" class=""><div dir="ltr" class="">Javier Soto</div></div>
_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>