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

Károly Lőrentey karoly at lorentey.hu
Mon Jul 18 14:06:01 CDT 2016


On 2016-07-18 16:15:10 +0000, Leonardo Pessoa via swift-evolution said:

> I believe sealed by default applied to functions makes the behaviour
> consistent with classes and allows for the same behaviour we wanted
> with this proposal. It would allows us to create subclassable classes
> in which we would be able to internally override a method but not
> outside the library and selectively allow other methods to be
> overriden. Final won't do it so if this is not the default behaviour,
> it will be necessary to introduce the sealed keyword so we can achieve
> this.

Can you give an example where you'd need such a sealed method, but the 
obvious pattern I described wouldn't work?

> It's inconsistent to have to explicitly open a class and
> explicitly seal its methods and vice-versa. It was my assumption that
> when we chose sealed by default with the proposal we were talking
> about everything and not just classes (I at least was talking
> everything).

This was my assumption as well, but then I thought things through. I 
suspect this might be what our friendly review manager was getting at 
when he gently lamented the lack of sufficient discussion on 
"overridable".

> Introducing "dynamic" or some other keyword to mark explicitly methods
> that should be overriden is just the same "open" with sealed methods
> by default would mean for public methods so it makes no difference to
> me.

"dynamic" is already in the language. It changes method dispatch to use 
Objective-C style message passing, enabling advanced techniques based 
on the dynamic runtime, such as method swizzling or KVO. Since it 
already exists, I'm not arguing for its introduction; I merely want it 
integrated into the proposal, in order to keep the language coherent.

> Also having no default will not change that some library
> developers will have everything sealed and selectively open.
> No default shall also make developers prone to open to think more about
> the keyword they'll choose to use,

Exactly! For this particular corner of the language, it fulfills the 
"unwavering goal of requiring additional thought when publishing a 
class as public API". Forcing people to make an explicit choice is by 
far the most straightforward way to achieve this. It also has the nice 
property of being harder to interpret as a value judgement on 
overridability, which is clearly a topic that gets people's monocles 
popping. ಠ_ರೃ

> but I'm not fond of no default.

If adding an extra qualifier turns out to be an onerous requirement, we 
can choose a default at any time later, without breaking existing code. 
It'll probably be easier to do so once we have a little experience in 
actually using the new language.

> As for the inheritance of openness, I firmly believe it shouldn't. If
> a class inherited from an open class is open by default, there will be
> no libraries making use of other libraries or we should also introduce
> the sealed keyword in order to make the class (or method) sealed
> again. This behaviour seems inconsistent to me too.

Agreed.

> 
> L
> 
> 
> On 18 July 2016 at 09:07, Károly Lőrentey <swift-evolution at swift.org> wrote:
>> On 2016-07-18 09:17:43 +0000, David Hart via swift-evolution said:
>> 
>>> On 18 Jul 2016, at 11:11, Xiaodi Wu via swift-evolution
>>> <swift-evolution at swift.org> wrote:
>>> 
>>> On Mon, Jul 18, 2016 at 3:27 AM, Brent Royal-Gordon via swift-evolution
>>> <swift-evolution at swift.org> wrote:
>>>> On Jul 17, 2016, at 8:57 PM, L. Mihalkovic via swift-evolution
>>>> <swift-evolution at swift.org> wrote:
>>>> 
>>>>> On Jul 17, 2016, at 9:14 PM, Garth Snyder via swift-evolution
>>>>> <swift-evolution at swift.org> wrote:
>>>>> 
>>>>> Is there a summary somewhere of the motivation for allowing methods to
>>>>> be declared non-overridable within open classes?
>>> [...]
>>> Garth: I think it's implicit in the reasons to prevent subclassing. The
>>> mere fact that a class allows subclassing doesn't necessarily mean that
>>> every member in it is designed to be subclassed. Consider
>>> `UIViewController`: It's obviously designed to be subclassed, and some
>>> methods in it (such as `loadView`) are intended to be overridden, but others
>>> (such as `loadViewIfNeeded`) are *not* intended to be overridden.
>>> 
>>> And [if UIViewController were to be written in Swift] there'd be a good
>>> reason why `loadViewIfNeeded` and others of its ilk couldn't be final?
>>> 
>>> I don't know UIKit internals, but I could imagine loadViewIfNeeded be
>>> overridden internally, if one knows the precise internal workings of
>>> UIViewController. That would require open, to allow overriding internally
>>> but not externally.
>> 
>> 
>> 
>> I thought about this aspect a little more. I think it's fair to say that
>> we're breaking new ground for language design here. Classes limiting
>> inheritance to a certain set of subclasses are nothing new (I've written &
>> used classes doing this in C++, Java and C#), but no language that I know of
>> allows limiting overrides of a specific public member in such a way. I think
>> we need a convincing rationale for making this esoteric middle ground
>> between final and open members the new default.
>> 
>> The UIKit example above isn't convincing at all. It is already quite easy to
>> allow package-internal subclasses to configure the behavior of
>> loadViewIfNeeded without such a novel language feature. E.g., the UIKit team
>> can simply make loadViewIfNeeded call into a non-final but internal method:
>> 
>> public open class UIViewController {
>> private var _view: UIView? = nil
>> 
>> public final func loadViewIfNeeded() {
>> internalLoadViewIfNeeded()
>> }
>> 
>> internal func internalLoadViewIfNeeded() { // overridable internally
>> if let view = _view { return }
>> loadView()
>> }
>> 
>> public open func loadView() {
>> // Load it from a nib or whatevs
>> }
>> }
>> 
>> I see no drawback to this pattern; it is quite clear and simple. Therefore,
>> in the interest of keeping the language free of needless complexity, I
>> suggest we change the proposal to remove the implicit "sealed" level of
>> public member overridability, and support only "open" or "final" class
>> members.
>> 
>> For members, "open" should mean the opposite of "final", with no levels in
>> between. Member-level openness should be entirely independent of visibility;
>> so it should be possible to say "internal open" to mean an internally
>> overridable member that's not at all visible outside the module -- the same
>> as today's default.
>> 
>> (Note that (on platforms with an Objective-C runtime) "dynamic" provides a
>> third level of flexibility for class members; I argue that it should imply
>> "open". So in order of increasing flexibility, we'd have "final", "open" and
>> "dynamic" members. This seems easy enough to describe and understand.)
>> 
>> I also suggest that for now, we should make neither "final" nor "open" nor
>> "dynamic" the default for public members of open classes: we should rather
>> require class authors to explicity add one of these qualifiers to all public
>> member declarations. This way, we can defer the argument for choosing a
>> default to a later (additive) proposal, once we have some experience with
>> this setup. Non-public members can safely keep defaulting to "internal
>> open", like they do today.
>> 
>> --
>> Károly
>> @lorentey
>> 
>> 
>> 
>> _______________________________________________
>> 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


-- 
Károly
@lorentey




More information about the swift-evolution mailing list