[swift-evolution] [Review] SE-0117: Default classes to benon-subclassable publicly

Károly Lőrentey karoly at lorentey.hu
Mon Jul 11 17:51:19 CDT 2016


On 2016-07-11 08:42:33 +0000, Tino Heth via swift-evolution said:

> 
>> The justification for this proposal is all about supporting the people 
>> who are working to design library APIs right, and about maintaining 
>> consistency with the design philosophy of Swift. To wit: in Swift, 
>> where there’s a default choice, it’s the safe one;
> I challenge this claim:
> Safety is valued, but Swift cares (and should care!) about pragmatism 
> as well… the most obvious example that comes to my mind are arrays, 
> which have no safeguards that stop you from accessing elements that 
> aren't there.

I'm not sure I understand this point. First, a guaranteed runtime trap 
on an out-of-bounds index is a *massive* safety improvement over what 
we had in C/C++/Objective-C.
Second, safety is explicitly goal #1 for Swift; it's even more 
important than performance and expressiveness (although fortunately 
these aren't always at odds).

>> where there’s a consequential design decision, it’s explicit.
> When there is no explicit statement about subclassiblily, it's 
> reasonable to assume that there hasn't been a consequential design 
> decision… but sadly, discussion like this mainly driven by dogmatism, 
> because there is no evidence for either side.

I've made some notes of the arguments (pro and con) that I've seen in 
this thread and elsewhere; see them below. I think we have seen ample 
evidence in support for most of them, from both sides. As usual, our 
differences are difficult/impossible to reconcile. People may have 
become a bit theatrical at times, but the discussions I've read have 
been far from dogmatic.

Thankfully we can rely on the core team to break the stalemate. (I 
think it's safe to say that despite all the drama, things will turn out 
OK either way. Things generally do.)

I'm firmly in the +1 camp, so take my summary of the arguments against 
the proposal with a grain of salt. For what it's worth, I am 
sympathetic to most of the -1 arguments; I just find them less 
convincing in contrast. Some of the labels are somewhat bombastic; 
sorry about that.





## Arguments for SE-0117

- Doctrine: Safety is the most important design goal of Swift, and 
having to opt-in to cross-module subclassing has been demonstrated to 
be the safer choice.

- Authority: We've seen that in some OOP communities, "Explicitly 
design for subclassing or else prohibit it" has been a well-known and 
highly regarded API design advice for decades.

- Precedent: Kotlin's designers made the (admittedly questionable) 
decision of making final classes the default; the world didn't end. 
(And SE-0117 proposes a much better approach than that.) Moreover, Rust 
and Go are examples of popular languages that have decided to 
completely get rid of (implementation) inheritance; again, the sky did 
not fall on the heads of their designers: these languages have grown 
quite popular regardless of this decision. (Note though that nobody is 
suggesting to remove subclassing from Swift.)

- Convenience: For small-scale library developers, it becomes a little 
easier to create and maintain well-designed reusable Swift modules, by 
not having to remember to disable external subclassing whenever a new 
public class is created. Large-scale framework developers like Apple 
will remain able to do whatever they want, regardless of this proposal.

- Sensible Defaults: Accidentally leaving a class sealed can be easily 
fixed in a point release; accidentally leaving a class open can only be 
fixed by a major API version bump, because closing a class may break 
existing client code. (Provided that you care about strict API 
versioning.) So it makes sense to make the less damaging choice the 
default.

- Design Variety: Allowing sealed classes (either opt-in or opt-out) 
makes it possible to make class-hierarchies public without the burden 
of preparing for external subclasses. This makes certain API design 
patterns easier to implement in Swift. Ironically, introducing sealed 
classes may thus make subclassing *more* popular in carefully designed 
Swift packages.

- Performance: Sealing classes by default may enable the compiler to 
eliminate dynamic dispatch in more cases where allowing overrides was 
never intended. This may sometimes lead to meaningfully faster code.

- Limited Scope: Because the proposal only restricts subclassing across 
module boundaries, and because Swift already has a clear bias towards 
value types and protocol-oriented programming, and because final is 
already in widespread use in the language, many or most Swift users 
would not actually be significantly affected by this change.

- Education: The proposal gently encourages Swift users to think about 
ways other than subclassing to solve problems. Previous experience with 
the complexity of subclassing has lead to it gaining something of a bad 
reputation. This has made it unfashionable in the trendier section of 
the programming community, who are always very enthusiastic to offer 
advice about better approaches, and some of whom aren't even shy about 
showing their monads in public.

- Auditing: Some coding conventions discourage the use of subclassing 
in public APIs. Modules that expose APIs where the user *needs* to 
subclass would become easier to spot if this proposal was accepted. 
Programmers who're particularly pedantic and/or battle-scarred would be 
able to easily spot open classes and treat them as an obvious code 
smell.



## Arguments against SE-0117

- Pragmatism: Working around bugs by monkey patching closed-source 
libraries becomes even harder to pull off than it already is. An 
ecosystem of carefully designed and bug-free modules is a pipe dream; 
in the real world, packages are quickly hacked together with little if 
any consideration to concerns of the ivory tower people. My 
dependencies are full of bugs that upstream authors cannot or will not 
fix in time for my next release.

- Extendability: Subclassing is an important way to extend third-party 
classes with new functionality, and this proposal would often remove 
people's ability to do this. Library authors will never be able to 
predict all the ways their code will need to be extended. Some 
important classes of functionality cannot easily be added via class 
extensions and/or composition. Library authors should expect no right 
to restrict the ways their library may or may not be used and extended.

- Senseless Defaults: The design as proposed arguably provides a slight 
benefit to authors of well-designed libraries, but it will make subpar 
libraries *much* worse. Lazy library authors will leave subclassing 
disabled even though they might not object against subclassing at all. 
This will lead to a plague of one-liner pull requests to open up 
classes.

- Tradition: Mainstream 80s/90s-era OOP languages invariably leave 
classes open by default. Newcomers would be shocked to find that Swift 
is doing otherwise. The proposal introduces a frustrating quirk into 
the language with at best marginal benefits in exchange.

- Annoyance: People will need to remember to manually add an extra 
keyword to their public classes whenever they want to allow 
subclassing. People who want to always allow cross-module subclassing 
will always have to add the keyword. The extra work to do this would be 
annoying and pointless, and the proliferation of such additional 
keywords would make Swift needlessly wordy.

- Unit testing: The common (if messy) Objective-C / Java approach to 
mocking an external class by subclassing it and overriding every method 
becomes even more impossible in Swift. (I can't recall anyone actually 
making this argument, but it would've been an interesting point.)

- Incompleteness: This proposal does not go far enough; if we prefer to 
go in this direction, nothing less than requiring a formal description 
of the subclass-superclass API will do. Enabling module-external 
subclassing by the simple addition of a keyword would be irresponsible.

- Slippery Slope: SE-0117 adds yet another entry to the already huge 
list of things in Swift that subtly or openly discourage people from 
subclassing. How far are we from someone seriously proposing to 
outright rip inheritance out of the language? Enough is enough. Stop 
with the anti-subclassing propaganda. Implementation inheritance is a 
hugely important core language feature whose popularity should be 
preserved and whose use should be encouraged and celebrated.

- Heresy: Some people don't like programming languages that take away 
their freedom to do however they please. People should be trusted to 
use their tools well; even if they're slightly dangerous. Freedom over 
bureaucracy!

- The Weirdo Argument: This list represents a very curious subset of 
Swift developers, who are very different to the vast majority of people 
using Swift. Normal people just want to get their work done, and they 
would be infuriated if such a change would be implemented. This list is 
a quite exotic bubble, and it can be fatal to assume that it is 
representative for the mass of developers that have to deal with the 
consequences of its discussions. Programming languages are best evolved 
in a strictly democratic manner.

* * *

Sorry if I left something off, or if I misrepresented an argument. 
(This is a huge thread.) I had some trouble making sense of the last 
two or three arguments, so they're especially unlikely to represent 
people's actual opinions.

-- 
Károly
@lorentey




More information about the swift-evolution mailing list