[swift-evolution] [Proposal] Sealed classes by default

John McCall rjmccall at apple.com
Tue Jun 28 17:32:13 CDT 2016

> On Jun 28, 2016, at 2:20 PM, Christopher Kornher <ckornher at me.com> wrote:
>> On Jun 28, 2016, at 2:31 PM, John McCall via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> On Jun 28, 2016, at 12:39 PM, Michael Peternell via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> Sealing classes by default is a terrible idea IMHO. Fortunately, my faith in the Swift core team is strong enough so that I cannot believe that this has a chance of ever being implemented at all :)
>>> Why do I think it's terrible? I do subclass classes even when they say that you shouldn't do it. I even monkey-patched a few classes in the past. Why? Not because I prefer hacking over clean coding, but to get the job done. Every few months or so I encounter a situation where such hacking attempts are the only feasible way to make something work. Or I use them during research activities or unofficial "feasibility studies". (If that's not the case for you, then maybe you are working in a different job than I. Monkey patching is rarely necessary if you make iPhone apps.) These are situations where everyone else just says "that's not possible". Yes, you can make that USB driver class stop spamming the console; yes, you can stop that library from crashing randomly.
>>> There is so much more to say on this topic. Sealing classes is also bad because it prevents forms of experimental coding. It doesn't solve a real problem. How often have I seen bugs where someone subclassed a class that shouldn't be subclassed? That's so rare. But if all classes are sealed by default this will lead to far worse problems. When a developer subclasses a class, he usually has a reason. There already is a `final` keyword in Swift, and a dev really thinks that a class shouldn't be subclass-able, he can use it already. And if I subclass it from Objective-C anyways, then maybe I know what I'm doing?
>>> And I'm not buying this "performance" argument. If you want better performance, write the hot functions in C or maybe even in assembler. The dynamic dispatch overhead is not the feature that makes programs bad or slow. It's quadratic algorithms (e.g. unnecessary nested loops), calling functions multiple times (e.g. if(a.foo.bar(x[2]) == "joo" || a.foo.bar(x[2]) == "fooo" || a.foo.bar(x[2]) == nil) { ... }), or just overly complex code: all of them have more impact than dynamic dispatch. Even worse for performance is unnecessary IO or using graphics APIs inefficiently. A good profiler can help a lot with these issues.
>>> Swift should be designed for the real world, not for an ideal world that does not exist and that will never exist.
>> The design intent of much of Swift is to allow "free" coding within a module/file/type while encouraging programmers to think more carefully about their public interfaces.  Module interfaces are the most serious and permanent of these interfaces.  It's in keeping with that for declarations to make only minimal public guarantees and require the programmer to be more explicit about the things they want to allow.
>> Your arguments about patchability and performance could be applied to every feature in the language.  They are essentially arguments for a fully-dynamic environment.
> see https://en.wikipedia.org/wiki/Straw_man <https://en.wikipedia.org/wiki/Straw_man>
What's the limiting argument?  Why is it critical for practical programmers to be able to arbitrarily patch someone else's code, but only at the point where it calls public methods on public classes?  That's a very strange idea in Swift, given how we encourage the use of structs and non-public types.

> Most OO languages do not seal seal modules (or the equivalent) by default. Forcing developers to add “final” does not seem onerous. If authors do not have time to audit their frameworks, the language should not pretend do it for them.

Again, this could be an argument for making everything public by default.  (Many OO languages essentially do.)  What's the limiting argument?

Swift takes a consistent line here: the default rule across a module boundary is the rule that reserves the implementation the greatest flexibility in the future.  Making classes final outside of the module gives the module the right to make the class final everywhere and to change the class's internal implementation to call different methods or change delegation patterns without worrying about how it affects a use-case that most programmers won't have considered.  If they have considered it, of course, it is trivial for them to write that down, just as it is trivial for them to make the class public to begin with.

>> There's no reason to single out subclassing; for everything you can imagine, one programmer's caution will be another's paternalism.  You're just making the argument here because you're used to a particular way of patching Objective-C code, one which will work regardless of this language decision;
> That seems personal.

If there's another reason why this only applies to subclassing, I can't see it.

>> but most of the old ObjC techniques (e.g. accessing ivars reflectively) already won't work on Swift code without unsafe operations, and subclassing is a very tiny part of that problem.
>> And no, we do not and will not accept that performance-sensitive code can only possibly be written in C or assembly, or that algorithmic performance is the only thing worth worrying about, even for the language implementation.
> Could many of the performance advantages of sealing could be gained through “whole application” optimization?T he benefits could far exceed what is possible with whole module optimization. It would probably be extremely difficult and hard to scale, but for packaged, signed applications, it should be possible some day.

Whole-program optimization relies on there being some step at which we can see all the code.  We want to allow separate compilation of libraries, and we do not have a JIT.

The decision to make class methods polymorphic by default was always premised on being able to undo that in obvious cases where methods are never overridden.  Making a class public so that clients can use it shouldn't cause significant performance degradations just because you forgot to also write "final".

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160628/1d1cb490/attachment.html>

More information about the swift-evolution mailing list