[swift-evolution] [swift-evolution-announce] [Review #2] SE-0117: Default classes to be non-subclassable publicly
Karl
razielim at gmail.com
Wed Jul 20 17:26:57 CDT 2016
> On 20 Jul 2016, at 11:07, Brent Royal-Gordon <brent at architechies.com> wrote:
>
>> On Jul 19, 2016, at 6:14 PM, Karl <razielim at gmail.com> wrote:
>>
>> That is to say, we basically introduce “open" as an inverted “final”, and make all classes non-open by default. That is analogous to enabling whole-module-optimisation by default, in my opinion. The cost of an extra four-letter word isn’t that great, but the benefits to readability and reasonability all-around make it worthwhile.
>
> Okay, but this ignores an important difference between sealed and final.
>
> Sealed is *non-committal*. It makes no promises to wider scopes about whether there are other subclasses/overrides; it merely states that code outside the module may not subclass/override. `final`, on the other hand, is an *affirmative* statement: there are no subclasses/overrides and there never will be. Code outside the module is permitted to rely on that fact—for instance, it can generate static calls and conflate dynamic Self with static Self in conformances.
>
> But this distinction only really makes sense at the `public` boundary, because that's where the compiler—and even the developer—faces an impenetrable encapsulation barrier. If you want to require `open` even on non-public declarations, you thus need to choose one of these designs:
>
> 1. `final` is still available, but only on public APIs.
>
> 2. All non-public classes have to be explicitly declared either `open` or `final`.
>
> 3. Sealed has some sort of complex, scope-specific design (for instance, an `internal` class can be subclassed in `fileprivate` but not in `internal` scope).
>
> Of these three, I think that #2 is overly bureaucratic and #3 is overly complicated, so only #1 is a viable option. And then we're just choosing whether we have internal subclassing by default and an odd public-only `open` keyword, or no internal subclassing by default and an odd public-only `final` keyword. No viable option avoids some kind of asymmetry at the `public` boundary.
>
> Open-by-default with a `final` keyword is the traditional design. It is simpler for people learning to program and more familiar for people new to Swift. It is more convenient. And you just don't need the same rigorous, formal definition of semantics in internal scope, where you can alter a problematic invariant rather than having to live with it.
>
> All that said, there most likely *is* value in declaring, even internally, which classes are meant to be subclassed and which members are meant to be overridden. Perhaps what we ought to do is permit `open` even on non-public APIs, but not enforce it (or rather, leave enforcement to linters). That would allow people and teams to explicitly document internal overriding behavior if they want to, without burdening the teams that don't want to go to the effort.
>
> --
> Brent Royal-Gordon
> Architechies
>
This is exactly what I'm talking about - this is actually a very simple discussion. Throwing around words like “non-committal” and “affirmative” and speaking abstractly doesn’t disguise the brunt of what you’re saying: limiting “open” to public classes only lets you be sloppy inside your own module. That’s the only reason to make it like that. If I were describing the concept of “sloppiness” while trying my hardest not to use the word itself, I would probably say pretty much what you just wrote - wanting to remain non-committal, avoid definite, affirmative statements, etc.
If we think it is important for people who write classes to locally reason about their code, it’s important full stop. The conflation between if it’s publicly accessible and whether or not is a staggeringly massive regression in read&reason-ability, no matter how matter how you want to phrase it. It’s a general problem which deserves a general solution - not some ‘non-committal’ proposal which can't even commit to the problem it is trying to help with.
If you’ve ever had to deal with complex base classes, consisting of a mixture of internal (and possibly internally subclassed) members and public ones, you would know that annotating which members are to be used by what (and knowing that it’s enforced by the compiler) would be a massive improvement to our language syntax. Sometimes you didn’t write all the code you have to work with, so it’s handy to have code which annotates this stuff.
Karl
More information about the swift-evolution
mailing list