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

L. Mihalkovic laurent.mihalkovic at gmail.com
Sat Jul 2 02:42:47 CDT 2016



Regards
(From mobile)

On Jul 1, 2016, at 6:43 PM, John McCall via swift-evolution <swift-evolution at swift.org> wrote:

>>> 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.

This is a situation I often run into in jave where I would use an enum to create a finite set of constants to be passed (say action identifers). But then this makes it very difficult for external modules to extend the core set of actions locally. So i generally windup with an enum and an interface (the enum implements the interface).
Then local extensions are free to define their own local enums to cover only their local extensions to the original core set of actions. Then the public api definition constrains the action parameter to be enum&TheRequiredInterface. In the end this is a pattern I've come to like because tracing usage of the interface I can find all subsequent entensions to the core set of actions. Each subsequent set of extensions is just that: its own closed enum showing that they all form a coherent namespace for that extension (being a different enum than my original set, they do not prevent me extending the core set without risking name collisions with their extensions). The only cost is the heavier api declation site signature that is wearing the double enum & interface constraint.
The point is that you can extend enums without have to open them, and the resulting pattern may be even better than if you open the enum. The only hickup..... Swift does not let you express

protocol Command{}
func myCommand Handler<C: enum & Command>{}


>> [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. :)
> 
> John.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution


More information about the swift-evolution mailing list