> > 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.
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.) Nevin On Wed, Jul 20, 2016 at 1:34 PM, John McCall via swift-evolution < [email protected]> wrote: > > On Jul 20, 2016, at 10:13 AM, Károly Lőrentey <[email protected]> > wrote: > >> On 2016-07-18, at 19:05, John McCall via swift-evolution < > [email protected]> 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 > boilerplate. > > > > 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". > > John. > > > > > We never have to spell "internal", but I think it is still very useful > that it exists. > > > > -- > > Károly > > @lorentey > > > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
