[swift-evolution] [Review #3] SE-0117: Allow distinguishing between public access and public overridability

John McCall rjmccall at apple.com
Fri Jul 22 11:36:46 CDT 2016

> On Jul 22, 2016, at 9:09 AM, Károly Lőrentey via swift-evolution <swift-evolution at swift.org> wrote:
> On 2016-07-21 15:33:37 +0000, Chris Lattner via swift-evolution said:
>> 	* What is your evaluation of the proposal?
> I gave enthusiastic thumbs up to the previous two proposals. I agree
> wholeheartedly with the underlying goal, and I love the new Motivation
> section. I'm OK with the idea of making "open" imply "public" by
> default, which seems to be the gist of the new design.
> However, reading the actual details in this third revision made me
> uncomfortable. I expected the specification to be bulletproof by now;
> but the actual text of the "Proposed design" section seems to be even
> less coherent than the previous two.
> I understand there's a (self-imposed) deadline, but this one could've
> really used a little more time in the oven.
> While I am still strongly in favor of the direction and high-level
> design of the proposal, I cannot in good conscience argue in favor
> of such ill-defined details in a _third_review_. So I'm giving
> this specific revision -1 in protest. 😢

I'm sorry if my drafting wasn't up to snuff.  In my defense, I needed to get it out quickly,
and I had a lot to rewrite.  I was pretty tired by the time I actually finished the motivation

> Here are some of my problems with the text:
> 1. The proposal offers two separate design approaches for `open` classes, listing
>  arguments for both. Please excuse my bluntness, but this is now the third attempt
>  to push this through: With all due respect, would you please make up your mind?

Most of the proposal has been "pushed through", frankly.  We are looking for feedback
on this specific question.  I agree that the proposal framework is an awkward fit for
this kind of discussion prompt.

> 2. The option to get rid of sealed classes is brand new with SE-0117rev3.
>  Did someone argue for it in review #2?
>  (I'm all for adding better support for sealed class hierarchies, so I prefer plan A.)
>  It's nice that we're given the option of reducing language complexity, but then
>  it's bizarre that the idea of eliminating sealed _members_ isn't mentioned at all.

Because it was settled in the core-team review.

>  Although, a literal reading of "plan B" does in fact seem to imply that any member
>  that is not explicitly marked open would not even be _internally_ overridable:
>     "The second design says that there is no such thing as an open class because
>     all classes are subclassable unless made final. Note that its direct methods
>     would still not be overridable unless individually made open, although its
>     inherited open methods could still be overridden."
>  There is no mention of "public", and no mention of module boundaries.
>  Thus, plan B basically proposes to make *all members* in *all classes* `final` by default.
>  This seems inconsistent with the rest of the proposal, so I'll assume this is a
>  mistake in wording, not the actual design intent. (Or is it?)

It is a mistake in wording.

>  Let's assume the intent was to keep members internally overridable by default.
>  But then why not go the full way? We can achieve the proposal's goals by just tweaking
>  existing defaults -- there's no need to introduce complicated new overridability levels:
>  "This proposal does not change the rules of class subclassibility. However, it introduces the
>  (previously implicit) "open" qualifier to explicitly declare an overridable member,
>  and changes the rules of default overridability as follows:
>    - Members of public classes are now final by default.
>    - Members of internal and private classes remain overridable ("open") by default."
>  I prefer plan A, but I'd also be OK with this bare-bones proposal.
>  I'm strongly opposed to plan B (either as stated in the proposal or what (I assume
>  above) is the intent behind it.)

Thank you, that's what we're looking for.

> 3. The code examples are needlessly complicated by trying to describe both sub-proposals.
>  I find this is confusing and it obscures the actual effect of both variants.

I'm sorry, I didn't have much of a choice about this.  Perhaps I could have broken it out
into two completely different code examples.

>  The examples are also inconsistent with the text: e.g., AFAICT, property `size` below
>  is still intended to be overridable inside the module that defines
>  `SubclassableParentClass`.
>      open class SubclassableParentClass {
>        // This property is not overridable.
>        public var size : Int
>        ...
>      }

Yes, this should read "not overridable outside of the current module".

> 4. The previous revisions ignored dynamic members, which wasn't great. The current
>  document acknowleges that "dynamic" members exists, but then *prohibits*
>  them from being overridden across a module boundary!
>     "`open` is not permitted on declarations that are explicitly `final` or `dynamic`."
>     "A class member that is not `open` cannot be overridden outside of the current module."
>  If this was intentional, I'd love to see the reasoning behind this decision.
>  Otherwise the wording should be fixed:
>     "A class member that is not `open` or `dynamic` cannot be overridden outside
>     of the current module."

Yes, sorry, this was a drafting error again.

> 5. It seems strangely inconsistent that `open` now implies `public` by default,
>  but `final` doesn't. The proposal fails to explain this inconsistency.
>  Was this point considered and dismissed, or was it not considered at all?

I think the best model of thinking about "open" is that it is an access level above "public".
The idea of allowing "internal open" came out of discussion but in retrospect does not
seem to hold its own.

>  Changing `final` to imply `public` later would be a source-breaking change.
>  Given that SE-0117 is one of the last proposals where maintaining source
>  compatibility isn't a major consideration, it's unclear if we'll ever have an
>  opportunity to fix such minor inconsistencies.
>  The same argument also applies to `dynamic`. If it's OK for `open func` to
>  imply public visibility, wouldn't it follow from same logic that we should
>  treat `final func` and `dynamic func` the same way?
> 6. The proposal does not suggest an explicit spelling for the new default level
>  of internal-only overridability for public classes and members.
>  We can add an optional contextual keyword later, in an additive proposal.
>  However, failing to mention this point makes me question if it was intentionally
>  omitted to prevent bikeshedding, or just forgotten.
> 7. "`open` is permitted only on class declarations and overridable class members
>  (i.e. var, func, and subscript)."
>  Presumably this list is to be taken to include `class func`, but not `static func`.
>  Or would `class func` remain open by default?

"class" members are still members.  The model for "static" on class members has always
been that they are implicitly final.

> 8. The opening clause in the "open class members" section makes no sense to me.
>     "A class member that overrides an open class member must be explicitly declared open
>     unless it is explicitly final or it is a member of a final or non-public class.
>     In any case, it is considered open."
>  So, AFAICU, when a public member of a public open class overrides a superclass member,
>  we will have to write either "open override" or "final override". Temporary restriction, fine.
>  (Shouldn't "dynamic override" be a thing, though?)
>  But if these are "considered open in any case", why force us to add misleading boilerplate?
>  What does it mean for a member of a final class to be "considered open"?

Today, you would have to write "public override" on such a member.  It is not added
boilerplate to say that you have to instead write "open override" unless your intent is to
close off overriding (in which case you have to write "public final override").

> 9. I have trouble interpreting the parenthesized statement in the following clause:
>     "`open` is not permitted on declarations that are explicitly `final` or `dynamic`.
>     (Note that it's okay if one or both of the modifiers are implicitly inferred.)"
>  Can "dynamic" ever be implicitly inferred?

It's inherited.  I can't remember if we require it to be explicit on the override.

> Can both "dynamic" and "final" ever apply at once?

This is currently forbidden when explicit, but you can declare a dynamic member of a final class.

> I assume "it's okay" here means `open` is allowed but ignored, in either case.
>  Is that right?

Probably the right rule is that open can be subsumed by dynamic, so that you can override
an open member and make it dynamic.  I'm not sure whether you should be able to say just
"open" on an override of a dynamic member when it clearly remains dynamic.

Inherited open-ness will be ignored on a final class or member.

Honestly, this is why I don't like to include this level of detail in proposals.  People complain if
it's not there, but including it just invites a bunch of complaints about every detail and distracts
from the important parts of the discussion.


> Ayayay, so much drama!
>> 	* Is the problem being addressed significant enough to warrant a change to Swift?
> Yes.
>> 	* Does this proposal fit well with the feel and direction of Swift?
> Yes.
>> 	* If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
> See previous reviews.
>> 	* How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
> Probably more than it deserves? ;-)
> -- 
> Károly
> @lorentey
> _______________________________________________
> 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