[swift-evolution] [swift-evolution-announce] [Review #2] SE-0117: Default classes to be non-subclassable publicly

Peter Livesey pdlivesey at gmail.com
Wed Jul 20 19:26:15 CDT 2016


+1 to Karl's suggestion

I've come around to the idea of not allowing subclassing by default (well,
50/50) because I rarely see final used in swift code (read: it's
underused). However, I think the special casing of 3rd party libraries is
weird.

Let's assume this is true: "It's really bad for developers to subclass
things which weren't intended to be subclassed by the developer who wrote
the class."

Given this, we want to turn off subclassability by default (reasonable). So
why is it ok for internal classes to incorrectly subclass things? The main
argument against this seems like it's hard for swift beginners to learn
this rule (but somehow, this proposal isn't?). Or that public APIs are
'more important' (disagree with this generalization. it totally depends on
the library/app. And even if they are more important, if it's better, then
it's better for everything). Am I missing other arguments for why this
would be bad for internal classes?


One other note:

*From and API perspective, *if the developer does everything right and
thinks about subclassing, there is no difference between this proposal and
what we have today. As in, the developer correctly decides to add
final/open (note: I know theres a different in the module itself, but from
an API perspective they are the same).

This proposal only matter when a developer "makes a mistake". It only
applies to developers who forget to take subclassing into consideration.

I've learned that you shouldn't optimize for bad developers. It seems like
adding complexity to the language and a new keyword just so when developers
make a mistake, it's not as bad seems strange to me. I think languages in
general should optimize for good code, not bad.

On Wed, Jul 20, 2016 at 4:53 PM ilya via swift-evolution <
swift-evolution at swift.org> wrote:

> >  limiting “open” to public classes only lets you be sloppy inside your
> own module.
>
> I don't find that idea bad actually.
>
> In the similar vein one can say "it's ok to be sloppy with local variable
> names, it's not ok to be sloppy with instance variable names".
>
> Again, tradeoffs. You can require that things are perfect, so that all
> methods are thoughtfully annotated, and all variables have wonderful
> self-descriptive names, but you'll get most bang for the buck by requiring
> that for the interface (public classes / instance variables) rather than
> for the implementation (non-public classes / local variables).
>
>
>
>
> On Thu, Jul 21, 2016 at 12:26 AM, Karl via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>>
>> > 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
>>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>
> _______________________________________________
> 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/20160721/496c9919/attachment.html>


More information about the swift-evolution mailing list