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

John McCall rjmccall at apple.com
Fri Jul 1 11:43:11 CDT 2016

> On Jul 1, 2016, at 2:08 AM, Brent Royal-Gordon <brent at architechies.com> wrote:
>> That starts to look an awful lot like a fifth access level just for classes (I know you're not proposing one, but it could start to look that way to a user).
> You know, it *could* be.
> Suppose that, in `internal` scope, you can do all of these things:
> * Subclass a class.
> * Add a case to an enum in an extension.[1]
> * Add a stored property to a struct in an extension.
> * Conform to a protocol (as opposed to just using and constraining with the existing conformances).
> * Override an individual initializer, property, subscript, or method in an extension or subclass.[2]
> But `public` does not permit them. You can *use* something that is public, but you can't extend it. `open`, on the other hand, *does* allow you to extend them. It means that outside code has (about) as much freedom to extend the `open` item as code inside your module does.
> This approach would allow us to make the "sealing" very tight—if you changed a class from `public` to `open`, all of its `public` members would still be sealed—without actually making that a heavy burden on programmers who want things unsealed.

Yes, this is the way I've been thinking about it.

> This also suggests that perhaps `final` is best thought of as foreclosing the things that `open` permits, either within a module or in future versions:
> * A `final` class can never be subclassed.
> * A `final` enum can never have cases added.
> * A `final` struct can never have stored properties added.
> * A `final` protocol...well, okay, that one's pretty useless. Maybe there just aren't `final` protocols.[3]
> * A `final` property, subscript, or method can never be overridden.
> A `final` thing would be fast both inside and outside the module, because it would have guarantees about its size/dynamic type/implementation; an `open` thing would be slow both inside and outside, because it would have no such guarantees; and a non-`final`, non-`open` thing would be slow externally but fast internally.

This is an interesting thought.  Its application to 'struct', though, seems a bit strange, since 'final' on classes doesn't remove the ability for the module to change the stored properties (and clearly we wouldn't want it to do so).

> [1] There's another thread floating around where I've seen people suggest that enums could never be open because you couldn't unique integer raw values to each case. I think that's a rather narrow view of what an enum is for; many, perhaps most, enums don't need raw values, and even those that do could support string raw values with little danger.

If we know that an enum needs to be extensible, we can leave room in its implementation for cases with arbitrary associated values.  This isn't a problem.

Raw values are perhaps different.

> [2] You can't currently override a member in an extension, but I think that would be essential for initializing extension stored properties and especially for adding cases to enums (you'd want to override members to handle your cases).

Yes, I think allowing extensions to override members is an eventual goal.  It needs runtime support, though, and some careful language design.

> [3] Or maybe a `final` protocol can't be conformed to directly, but only through a sub-protocol (which could only be defined within the module). That would allow arrangements like "Sequence is the parent protocol of both IteratorProtocol and Collection, but all Sequences must conform to one of those sub-protocols".

It's okay for a modifier to not have meaning for absolutely everything. :)


More information about the swift-evolution mailing list