[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