[swift-evolution] [Review #3] SE-0117: Allow distinguishing between public access and public overridability
Károly Lőrentey
karoly at lorentey.hu
Fri Jul 22 11:09:06 CDT 2016
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. 😢
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?
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.
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?)
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.)
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.
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
...
}
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."
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?
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?
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"?
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? Can both "dynamic" and
"final" ever apply at
once? I assume "it's okay" here means `open` is allowed but ignored,
in either case.
Is that right?
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
More information about the swift-evolution
mailing list