[swift-evolution] [Proposal] Sealed classes by default

Michael Ilseman milseman at apple.com
Mon Jun 27 18:25:56 CDT 2016


Could you elaborate on how we should treat classes imported from Objective-C or CF-style C? That is, do we always annotate them as being “open” because those paradigms permit subclassing anywhere, or do you propose some kind of recommended “sealed” audit, or what?

> On Jun 27, 2016, at 3:40 PM, Javier Soto via swift-evolution <swift-evolution at swift.org> wrote:
> 
> Hello!
> 
> I sent this as a PR <https://github.com/apple/swift-evolution/pull/376> on the swift-evolution repo, but we never had any discussion about it on-list, besides a long time ago <http://thread.gmane.org/gmane.comp.lang.swift.evolution/9702/focus=9708>. Here's the first draft of the proposal:
> 
> 
> Sealed classes by default
> 
>  <https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#introduction>Introduction
> 
> Introduce a new sealed class modifier that makes classes and methods final outside of the module they're declared in, but non-final within the module.
> 
>  <https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#motivation>Motivation
> 
> 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 Liskov substitution principle <https://en.wikipedia.org/wiki/Liskov_substitution_principle>.
> Defaulting to non-final allows the author of a class to accidentally leave the visible methods open for overrides, even if they didn't carefully consider this possibility.
> Requiring that the author of a class mark a class as open is akin to requiring symbols to be explicitly public: it ensures that a conscious decision is made regarding whether the ability to subclass a class is part of the API.
>  <https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#proposed-solution>Proposed solution
> 
> New sealed (actual name pending bike-shedding) class modifier for classes and methods which marks them as only overridable within the module they're declared in.
> sealed becomes the default for classes and methods.
> New open (actual name pending bike-shedding) class modifier to explicitly mark a class or a method as overridable.
>  <https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#detailed-design>Detailed design
> 
> Code Examples:
> 
> /// ModuleA:
> 
> /// This class is `sealed` by default.
> /// This is equivalent to `sealed class SealedParentClass`
> class SealedParentClass {
>     /// This method is `sealed` by default`.
>     func foo()
> 
>     /// This raises a compilation error: a method can't have a "subclassability"
>     /// level higher than that of its class.
>     open func bar()
> 
>     /// The behavior of `final` methods remains unchanged.
>     final func baz()
> }
> 
> open class OpenParentClass {
>     /// This method is `sealed` by default`.
>     func foo()
> 
>     /// Overridable methods in an `open` class must be explicitly marked as `open`.
>     open func bar()
> 
>     /// The behavior of a `final` method remains unchanged.
>     final func baz()
> }
> 
> /// The behavior of `final` classes remains unchanged.
> final class FinalClass { }
> /// ModuleB:
> 
> import ModuleA
> 
> /// This raises a compilation error: ParentClass is effectively `final` from
> /// this module's point of view.
> class SubclassA : SealedParentClass { }
> 
> /// This is allowed since `OpenParentClass` has been marked explicitly `open`
> class SubclassB : OpenParentClass {
>     /// This raises a compilation error: `OpenParentClass.foo` is
>     /// effectively `final` outside of `ModuleA`.
>     override func foo() { }
> 
>     /// This is allowed since `OpenParentClass.bar` is explicitly `open`.
>     override func bar() { }
> }
>  <https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#impact-on-existing-code>Impact on existing code
> 
> 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 open.
>  <https://github.com/JaviSoto/swift-evolution/blob/a46877afb0302d2b03fa493255f5ced04ccb7f34/proposals/0000-sealed-by-default.md#alternatives-considered>Alternatives considered
> 
> Defaulting to final instead: This would be comparable to Swift defaulting to private, as opposed to internal. Just like internal is a better trade-off, sealed 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.
> -- 
> Javier Soto
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160627/ce49bd10/attachment.html>


More information about the swift-evolution mailing list