[swift-evolution] [swift-evolution-announce] [Review #2] SE-0117: Default classes to be non-subclassable publicly
nevin.brackettrozinsky at gmail.com
Wed Jul 20 13:36:19 CDT 2016
> However, once we've got non-open public classes — and as I understand it,
> you still support those — that complexity already exists. You're not
> really eliminating anything by preventing this from being applied to
We may not need to prevent public members from being declared non-open,
however I think that by *default* public members should be open.
Notably, one of the primary reasons for opening a class is to allow its
members to be overridden outside the module. In the by-all-accounts-rare
scenario that one or more public members of an open class should be
non-open, then they can be marked as such (or just use Károly’s trick with
`final` forwarding to `internal`).
I am reminded of the `atomic` / `nonatomic` situation in Objective-C. Yes,
`atomic` is generally safer, but almost every property in almost every
class is declared `nonatomic`. It was a mistake to set `atomic` as the
default, which caused a profusion of noise in property declarations.
If we were to make public members default to non-open, the result would be
similar: an extra keyword (`open`) appearing in almost every public
declaration in almost every open class.
The 80-20 rule may be applicable here: if even 1 out of 5 public members
ought to be non-open, then perhaps a consistency argument could be made.
But with non-open members being an essentially negligible fraction, I do
not see anything gained by *de facto* changing the spelling of `public` to
`public open` for members of open classes.
In sum, the default for public members should probably be `open`.
(I am fully on board with public *classes* defaulting to non-open.)
On Wed, Jul 20, 2016 at 1:34 PM, John McCall via swift-evolution <
swift-evolution at swift.org> wrote:
> > On Jul 20, 2016, at 10:13 AM, Károly Lőrentey <karoly at lorentey.hu>
> >> On 2016-07-18, at 19:05, John McCall via swift-evolution <
> swift-evolution at swift.org> wrote:
> >> The basic effect of Károly's counter-proposal is that every public
> member of an open class has to be marked either "open" or "final". That's
> > My primary point was that there is no need for a middle ground between
> "final" and "open" members.
> > I want to push my primary point a little more, so let’s forget my
> secondary suggestion to have no default, and let’s set an implicit choice.
> > I'd still argue for having no middle ground. “final” seems to be a good
> default; its effect matches the proposal.
> >> I think you and Károly are evaluating the addition of non-open methods
> as if they were being added primarily to increase expressive capabilities.
> They do marginally increase expressiveness, but I agree that it's not a
> common situation to explicitly want. However, neither are non-open classes.
> > It's more of an Occam's razor thing. The proposal prevents people from
> unintentionally exposing a wider API area than they intended. I agree with
> this wholeheartedly. I just don't believe that we need to add a brand new
> access level for members to achieve this goal.
> > Having an implicit "sealed" class level is a much easier sell for me,
> because it is sometimes desirable to expose a sealed class hierarchy, but
> Swift doesn't currently support it well -- AFAICT not as an intentional
> choice, but rather as an unfortunate side-effect of the initializer rules.
> You could've simply chosen to propose making "final" the default for public
> classes. Kotlin's troubles mostly wouldn't apply as long as internal
> classes would remain open, so I'd have supported that too. But rather than
> this, the proposal is built on top a nice solution to the sealed class
> problem. Solving it is obviously a good idea, and it is closely related to
> the goal of the proposal.
> > There is no such language problem in Swift 2 with sealed methods: an
> internal open member is sealed by virtue of not being externally visible.
> It’s straightforward to add a public final trampoline in the rare case when
> a sealed member should also be made externally callable. I believe the
> proposal works perfectly well without adding a language feature for this
> uncommon usecase.
> >> The goal here is not to create new expressive power, it's to establish
> a comprehensible intermediate position that's acceptable as a default so
> that publicizing an API doesn't require so much annotation and
> bookkeeping. Otherwise, programmers are forced to immediately decide
> between over-promising (by making the method publicly overridable) or
> breaking their own code (if they have internal overrides).
> > But making API public should never be done in a hurry. It includes
> making the API presentable, which involves some amount of refactoring.
> Granted, if an API has internally overridden methods that the author wants
> to make public but sealed, then they'd need to refactor these methods. But
> given how rare this is, and how easy it is to implement the trampoline
> pattern, is such a trivial refactoring step really too much to ask? (There
> are a number of much more complicated refactorings involved in making an
> API public; e.g., it is often the case that a method I want to make public
> has a parameter or return value with a type that I wish to keep internal.)
> I agree that having the concept of "visible publicly but only arbitrary
> modifiable internally" adds complexity to the language. However, once
> we've got non-open public classes — and as I understand it, you still
> support those — that complexity already exists. You're not really
> eliminating anything by preventing this from being applied to methods.
> Also, we're going to be proposing a lot of new things for
> library-resilience over the next six months or so that will add appreciable
> but unavoidable complexity to the language around module boundaries.
> Module boundaries have a lot of special significance in the language design
> because Swift takes the stable binary interface problem much more seriously
> than, I think, almost any other language can claim to.
> > I believe that apart from this one little wrinkle, the behavior that
> SE-0117 proposes can be fully implemented by allowing just "final", "open"
> and "dynamic" members, with "final" being the default for public members of
> open classes, and "open" being the default for all other members (including
> non-open classes).
> > Is smoothing out that wrinkle worth introducing a whole new default
> level of member overridability? I think this is worth some more discussion.
> > Note that if we end up with "final” members by default and it turns out
> to be the wrong choice, changing the default to sealed would not be a
> source-breaking change.
> >> Furthermore, I don't agree that non-open methods add significant new
> complexity. For clients of a library, a non-open method is final; there
> are no semantically-detectable differences (ignoring covariant overrides).
> Within a library, non-open methods remove the need for some unnecessary
> bookkeeping. And just on a conceptual level, the analogy to class behavior
> is quite simple.
> > This reminds me: Whether or not we allow the sealed level on methods, I
> suggest we provide a contextual keyword to (optionally) spell it. A
> "sealed" keyword is the obvious choice. This would encourage people to use
> common terminology, and makes it easier to use search engines to find an
> explanation of the concept. Autogenerated API summaries should add the
> "sealed" keyword.
> Yes, we should probably add some way to spell it. "sealed" does not feel
> like a natural opposite to "open", however, and I think we're quite taken
> with "open". I would suggest "nonopen" or "closed".
> > We never have to spell "internal", but I think it is still very useful
> that it exists.
> > --
> > Károly
> > @lorentey
> swift-evolution mailing list
> swift-evolution at swift.org
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the swift-evolution