Swift Classes to Be Non Publicly Subclassable by Default
SE-0117 (original, revision 1, revision 2):
The major observation here is that not all classes make sense to subclass, and it takes real thought and design work to make a class subclassable well. As such, being able to subclass a public class should be an additional “promise” beyond the class just being marked
public
. For example, one must consider the extension points that can be meaningfully overriden, and document the class invariants that need to be kept by any subclasses.Beyond high level application and library design issues, the Swift 1 approach is also problematic for performance. It is commonly the case that many properties of a class are marked
public
, but doing this means that the compiler has to generate dynamic dispatch code for each property access. This is an unnecessary performance loss in the case when the property was never intended to be overridable, because accesses within the module cannot be devirtualized.
As expected, this proposal was extremely polarizing, with valid arguments on both sides. The opinions held by supporters and opposers are held very strongly, and hundreds of emails were generated in a healthy debate about this topic.
[…]
On the first point, there are three related arguments against SE-0117:
First is that clients of Apple frameworks often choose to subclass classes that Apple publicly documents as being “not for subclassing”, as a way of “getting their job done,” typically as a way to work around claimed bugs in Apple frameworks. The core team and others at Apple feel that this argument is analogous to the argument that Swift should “support method swizzling by default”. Swift loves dynamic features, but has already taken a stance against unanticipated method swizzling, by requiring an API author to indicate where they allow method swizzling with the ‘dynamic’ keyword.
Yes, it’s absolutely analogous. It’s become obvious that “unanticipated” dynamism is not something that Apple wants to support with Swift. It’s clearly not Swifty, if by Swifty you mean the vision of the core team.
Second is that clients of some other public API vended by a non-Apple framework (e.g. a SwiftPM package) may end up in a situation where the framework author didn’t consider subclass-ability, but the client desires it. In this situation, the core team feels that a bigger problem happened: the vendor of the framework did not completely consider the use cases of the framework. This might have happened due to the framework not using sufficient black box unit testing, a failure of the imagination of the designer in terms of use cases, or because they have a bug in their framework that needs unanticipated subclass-ability in order to “get a job done”. Similar to the first point, the core team feels that the language is not the right place to solve this problem. Instead, there is a simple and general solution: communicate with the framework author and get them to add the capabilities that you desire.
Yes, there’s a bigger problem, and, yes, the language can’t “solve” this problem. That doesn’t imply that the language should forbid building a temporary workaround. In my view, the language should be helping to get the job done, not enforcing an idealized solution that may not even be possible because the vendor may be busy, uncooperative, or no longer available.
Third is a longer-term meta-concern, wherein a few people are concerned that future pure-Swift APIs will not consider subclass-ability in their design and will accidentally choose-by-omission to prevent subclass-ability on a future pure-Swift API (vended by Apple or otherwise). The core team feels that this is an extremely unlikely situation for several reasons. First of which is that it heavily overlaps the first two concerns. More significantly, any newly-designed and from-scratch APIs that are intended for Swift-only clients will make use of a breadth of abstractions supported by Swift—structs, enums, protocols, classes.
Indeed, this proposal is just one piece of a large trend. Swift is chipping away at dynamism for classes: subclassing, swizzling, hiding private classes and private methods. And the future, it appears, is APIs that rely more on structs, enums, and protocols, which are even more static. There are definitely some benefits to this direction, but I am concerned that it will lower app quality over the long term. This specific proposal may not have a huge impact because it affects neither Objective-C APIs (past) nor non-class Swift APIs (future).
To reiterate, as a summary, the core team agrees with conviction that it is the right default for public classes to be non-subclassable outside their module, unless they carry some additional indication by the API author that the class was designed to be subclassed.
Here’s my slightly exaggerated summary of the two points of view:
Me: I agree that dynamism shouldn’t be abused, but there are library bugs and limitations, and sometimes we need these tools to cope with them. I hate coding these workarounds and would love to remove them and unclutter my code as soon as the underlying issues are resolved. But bugs that affect the customer are much worse than temporary ugly code. Of course, ideally library vendors wouldn’t ship any bugs, but this is the real world. Nudging the API author to “think carefully” is well-intentioned but naive. The proposed solution is worse than the problem.
Apple: Making library vendors opt into dynamism will encourage them to think more about their designs. They will foresee how their APIs will be used. Making it impossible for developers to work around library bugs will foster communication with the vendor, who will then fix them. The fixing will be easier because the vendor won’t have to take into account developers’ workarounds. We don’t particularly care if your app is broken for months or years while waiting for a bug to be fixed. Developers won’t feel pressured to work around library bugs because competing apps will be subject to the same restrictions.
Here are some of the more interesting comments from the discussion:
I do not want to be constrained by authors of libraries or frameworks into interacting with a system in only the ways they forsee. By making the default be non-subclassable, if a designer does not put thought into all the ways a class can be used then I as a consumer of the library am penalized.
Another point I can think of is mocking in tests. I’m unaware of any fully featured mocking solution in swift, and subclassing a class to override methods for mocking purposes is one way to make unit tests work with third party dependencies. If a class was not designed for subclassing, then writing tests where the class’s behavior needs to be suppressed would not be possible.
[…]
I’ve had issues before when working with third party C# code where the author didn’t declare a method as virtual, but the problem I was trying to solve required me to override that method. Luckily the code was open source, so I just embedded and edited the source into my project instead of using the prebuilt binaries, but that becomes a maintenance headache, and it is not possible for components that do not make the source available.
Can’t really help for feel like it is training wheels all around… or padlocks on every kitchen cupboards.
It’s not possible to accidentally override a method like it is in java or objective-c, so the proposal won’t help people who might accidentally override something. If a developer tries to override a method or class they probably have a specific reason in mind for doing so, and taking away the options that this ability presents, by nature of it being the default behavior, doesn’t sit well with me as it has the potential to take away a good tool from my toolbox.
I agree that API authors should have the power to restrict this dimension of a type’s usage, but I feel that it should be a conscious choice to do so.
Karl:
I really enjoy designing elegant, well-defined APIs and this behaviour for classes has bothered me for some time.
On the other hand, I have also subclassed things that weren’t meant to be subclassed (e.g. I believe UITabBar was an awkward one back in the day, and UINavigationController has so many broken behaviours it’s almost a requirement to subclass and patch it, even though I believe Apple disapproves). I could fall back to Objective-C, but that’s an implementation detail. In some theoretical future with an all-Swift UIKit, what do I do about those issues?
After thinking about it, I decided that in this theoretical future, no App would be able to use those hacks, and so all Apps would have the same broken behaviours and degrade the quality of the platform to such an extent that Apple is forced to do something about it. Ultimately, that’s exactly what we want; they need to see broken Apps everywhere in order to prioritise fixes. That improves code quality all-around.
I can’t fight the feeling that many fans of ideas like this believe that good designed libraries come for free by simply adding restrictions — which, at least in my opinion, just isn’t true: No matter what the defaults are, good libraries are hard to build, so I predict this proposal would not only fail in increasing framework quality, but also will make it much harder for users of those frameworks to work around their flaws, which are just a natural part of every software.
I think it’s a step backwards. […] It’s not bad to allow sealing of classes. I don’t see real value in it though. And “sealed” should not be the default. Either the class if final or not: for me that’s a valuable distinction. “sealed vs. unsealed” is not.
In all the OO languages I’ve used, classes are open by default. That raises a question: is this really a good idea? Aren’t we ignoring established precedent? What about all the unauthorized subclassing we’ve done in the past?
I’m sympathetic to those accustomed to Objective-C, Ruby, Javascript, Python, and other languages with more fungible type systems who argue that it’s unreasonable to expect us all to use classes only as library authors intend. Monkey patching has saved many a day, they’d say. And it’s true, it has! I rely on it.
My counterargument: the ecosystem is changing.
[…]
The big exception to this is Apple itself, and the direction of this proposal implies a large cultural shift in how Apple deals with its developer community. Having the Swift language so aggressively prevent the dubious, brittle, bad-idea-but-it-gets-the-job-done workarounds that the Obj-C runtime allowed means that Apple’s platforms are going to have to be more consciously extensible, more transparent in their design decisions, and more responsive to unanticipated needs.
With “sealed by default”, the situation changes: Users are protected from some simple bugs, but nonetheless, they’ll encounter situations where the library doesn’t do exactly what they want. So, you take a look at the source, find the problem, and fix it. It’s no bug at all, it’s just a tiny degree of freedom that is missing because it wasn’t important to the author. You can create a pull request as well, but it doesn’t offer a real improvement to the author, who is already busy with dozens of similar requests by other users -> you end up with a custom branch with all the housekeeping associated with it.
So overall, I’m quite sure that the proposal won’t improve software quality, but rather degrade it.
I am afraid that developers will not take the time to specify which methods are overridable resulting in libraries that are difficult to patch, extend.
In my 26+ years of object-oriented design and programming (other languages, Objective-C since 1990 and Java since 2001) I have worked with object oriented libraries and subclassed methods that the authors probably never anticipated. I have been able to fix problems, enhance classes by creating subclasses with fixes and enhanced behavior.
In java for example I have seen that sometimes I would have been able to fix bugs or enhance the existing classes had the author not chosen a method to be protected or private. Sometimes they had a good reason but sometimes they didn’t. Is have been able to survive using an awesome library that was discontinued and end-of-lifed thanks to subclassing that has allowed me to fix problems and enhance over the years as the Java language kept evolving.
I agree that subclassing library classes that were not explicitly planned to be subclassed might sometimes lead to bugs. But just because a technique or feature of a language allows you to sometimes make mistakes, it doesn’t mean it should be completely disallowed. By this logic, we’d have to remove all instances of "!" any anything related to pointers from Swift. It’s possible for a feature to allow you to shoot yourself in the foot, and still be useful at the same time.
[…]
I don’t think the problem is significant, and I’m afraid the proposed solution is worse than the problem itself.
I don’t think this proposal will achieve its stated objective of forcing people to think about subclassing more. It will just add confusing boilerplate.
Things like Swift optionals work well because they make the (often forgotten) choices explicit in the context that they are used. In the world of Human Factors, we call it a forcing function. This proposal has the inverse structure, and will be ineffective, because the “forcing” part of it shows up in a different context (i.e. trying to use a framework) than the decision is being made in (writing the framework). This type of thinking leads to things like Java and the DMV.
[…]
Those who were prone to be thoughtful about their design would have been anyway. Those who are not thoughtful about their design will just leave these annotations off… leaving us with no recourse to extend/modify classes. When people complain, they will add the annotations without actually thinking about the meaning (i.e. stack overflow / the fixit tells me I need to add this word to make the compiler happy). All this does is put framework users at the mercy of the framework writers.
I […] give a strong, strong, strong -1 to this proposal. To make classes non-subclassable by default is only going to lead to unanticipated pain and frustration. Also agree with other comments that subclassable and overridable conflate access control with class behavior. If we want to make it possible to define a class as non-subclassable to external users, I’d agree to something more consistent with existing swift access control like public(final) as has been proposed by other commenters. However, I agree that making classes final by default is a bad idea that will create a larger problem that it solves.
I can’t count the number of times it save my hours [to be] able to override arbitrary classes and methods.
Sometimes to simply add log point to understand how the API work. Other times to workaround bugs in the library. Or even to extends the library in a way that the author did not intent in the first place, but that was perfectly supported anyway.
I already see how libraries author will react to that new default. They will either don’t care and mark all classes as subclassable, or find to burdensome to get subclassability right and prohibit subclassing all classes.
Nonetheless, being able to subclass any class allowed me to solve a bunch of very bad memory leaks in the new NSCollectionView implementation, and made it usable for my project.
Thinking than you will have to work only with perfectly design libraries without any bug is utopian. And thinking you can predict every usages of your library by users is short sighted IMHO.
I sympathize with the argument about wanting to fix bugs and add features via override, but that’s never been maintainable in the long term; you always just end up with superclasses that everyone is terrified to touch because every subclass has its own invasive “fixes”, and that’s even when working within a single codebase. With libraries, you can pretty quickly get locked in to a specific version because your customizations don’t work with new releases; either that, or the maintainer just decides that they can’t fix of their mistakes and so goes off to rewrite it from scratch. Either way, it’s not good for the ecosystem.
Thirdly, this is a huge breaking change. Not only is it a breaking change, but for a lot of people, the breakages will be outside of their control. Consider if I publish a module with a class with public methods and you subclass it in your code. Once this change is implemented, my code will still compile and pass its unit tests but your code is now broken and you are dependent on me changing my code to fix your code.
Defaults matter, because they transmit a message:
Every rule and obstacle we add to Swift is a statement that says “we favor bureaucracy over freedom”, and this will affect the community evolving around the language.
When you use a library in a way that wasn’t anticipated by its author, you’ll ran into issues occasionally; nonetheless, I think we should struggle for an open ecosystem that encourages others to experiment and fail, rather than to limit their possibilities in a futile attempt to protect them.
I’ll just note that this proposal reminds me of the fortune file entry, “Whenever you see a sign that says ‘no exit’, it means there is an exit there.”
Specifically, under this proposal, whenever you see a nonsubclassable, non-final class (which I suppose would in fact be any class not explicitly marked as subclassable), it would mean that the library implementor IS in fact subclassing it. Otherwise, it would just be final. Therefore, it’s by definition a perfectly reasonable class to specialize. The implementor just doesn’t want YOU doing it.
Maybe the implementor has a very good reason for this. But in the modal case, probably not.
[…]
There should absolutely be a way to say “don’t subclass this.” But do you really need anything more than a comment that says “don’t subclass this?” Perhaps as a compromise, and as a concession to the value of automated checking, this feature could be reformulated as a warning system. A class marked as nonsubclassable could be extended only be including a corresponding acknowledgment keyword in the extending class, a kind of Swift version of -IHaveBeenWarnedThatAPFSIsPreReleaseAndThatIMayLoseData.
Because of that experience, I am fully convinced that library authors will never imagine all the ways developers will use a library’s API.
[…]
I also think it would eventually force a second change; some sort of override to the default, at which point we’ve extended the language further to get closer to where we started.
Personally, Im not against sealed by default, but I think there are cases where closed source libraries have certain cases where workarounds are necessary, and just sealing by default will prevent those cases.
One could say, “well just use an open source one, or change vendors” but its not that easy in The Real World™ where we get certain SDKs shoved down our throats by the suits… and while that may be a separate issue to the one at hand, its still a problem that won’t resolve itself by simply locking down things…
In my own case, Ive fought with NSBrowser / NSTreeController in the past and the only way to resolve things was to subclass (and no, waiting 1 or 2 years for a fix is not acceptable if you already have a product in the wild).
Please stop saying that this proposal will bring more consideration to the design of libraries. It isn’t true. I haven’t even seen an argument for why it would be true, it is just taken for granted that it is true.
[…]
It sounds good, but at the end of the day, people are human and they will make mistakes. Best to either catch those mistakes in the context where they happen or to mitigate the effect of it. This proposal basically forces you to feel the full effect of other people’s mistakes (thinking that it will discourage them from making them in the first place).
[…]
The current proposal will only cause massive problems down the line, IMHO. We will find an escape hatch is needed, but we will have made optimizations based on assumptions of finality which prevent us from easily adding one.
There are IMO no advantages to sealing by default. If there were, I cannot imagine how they can have so consistently eluded the designers and maintainers of so many great OOD languages in the last 30 years. Does it mean it is just a matter of time for the core team to take it the c++ standardization committee to make sure C++ gets enhanced the same way?
After 30 years of Objc’s msg dispatching being touted as not a performance problem, all we hear for the last couple WWDC (did you pay attention to the ‘they are getting it’ during one of the session) is how much every single dynamic dispatch must be chased out of our systems, culminating with this ‘seal by default is best for you’. Why can’t developers [writing] code be made responsible for writing well or not?
There is also a dangerous difference between helping the programmer catch mistakes (e.g. don’t accidentally subclass the wrong method) and trying to prevent them from coding in a style you disagree with. I have been seeing far to many proposals of the second variety of late.
It is the opposite of nearly every other OOP language’s behavior and the opposite of what anyone will expect who is coming to Swift after doing any OOP programming anywhere else. While I believe that Swift should be free to buck the trends of other languages where there are significant advantages, changing something this fundamental will introduce a new learning curve for even experienced programmers. The advantages better be significant, otherwise you are introducing a frustrating quirk into the language without any great benefit in exchange. But the advantages of default non-subclassability are marginal at best.
[…]
The developers who don’t make careful design decisions will just go with the default - in this case they will just leave the default in place, and their public classes will not be subclassable. That doesn’t improve anything, it just makes sloppy, poorly written code harder to fix in the off-chance that you are stuck working with it. In other words, quality will not improve, but productivity will suffer, because it will be harder to develop workarounds for the problems that will inevitably appear in even the best-designed APIs.
[…]
But this whole issue of default non-subclassability doesn’t fall into that category of “safety” at all. It doesn’t help a programmer produce safer code. The same hidden flaws will persist whether non-subclassability is the default or not. The difference is that those same hidden flaws will be more difficult to deal with after the fact.
The “final should be default because adding final after the fact is destructive” arguments are interesting, but ultimately not convincing to me. If you thought your class was safe for subclassing, but it ultimately wasn’t, this solves nothing. You’ll mark your class as subclassable, and you will ship it, and there will be issues, but it will still be too late to take things back. If you know your class is not safe for subclassing, you should mark it as final. There is no advantage here in that scenario.
This is all part of building a safe public API. Public API design can be difficult, but if you don’t understand safe subclassing designs, you are likely to miss the issues and mark your class as subclassable and ship it anyway. Again, the best way to tackle this is to find better ways to surface the issues. Making final the default still doesn’t solve the core issues of people not understanding the right design.
My point is that you are completely ignoring an entire class of risk that has a real-world $$$ cost. Every time I have to use a framework under this proposal, I am now completely at the mercy of the author. In the case of open source frameworks I can at least make a fork, but for closed source frameworks (often from large companies where us using their framework has been negotiated by the bosses) you have now just added weeks to my development cycle while I wait for big-company-who-doesn’t-really-care-that-much to update their stuff.
[…]
Are you offering to explain to my boss/client why I can’t add the feature in a reasonable timeframe like I can with Objective C frameworks? That it may not even be possible now in Swift even though the Android guy just did it in a few hours?
Do you know what I am going to tell my boss/client? “Don’t use Swift for frameworks” and “Try to avoid partnering with companies that have Swift frameworks”. “It is too much of a risk”. “We are giving them too much control over the future of our product…” I mean, it even affects the prices that companies can charge for crappy frameworks. If I am asking for a bunch of features that I need them to add to provide a basic feature, that affects negotiations/price (vs a world where I can add it myself if needed). Sealed-by-default gives them leverage.
The argument shouldn’t be “we need open subclassing or we can’t fix broken APIs”; that’s a false choice. It should be “we need something to fix broken APIs”, and that “something” should be proposed and the need for it should be argued in its own right.
-1 for this proposal, but +1 for solving the issues it raises
Regardless of what ends up being the defaults, I’m a very strong -1 on conflating visibility and subclassability/extendability.
I completely agree, the programmer should have as much power as we can give them, even if it means allowing them to shoot themselves in the foot, because at the end of the day Swift isn’t an academic exercise, it is a real world programming language, so it should be optimized for solving real world problems not having some kind of technical, philosophical, and stylistic perfection when those come at the cost of the former.
Update (2016-07-17): Daniel Jalkut:
It’s absurd to imagine you can predict all valid subclassing patterns. […] Apple, nor any framework provider, can provide functionality sufficient to cover the breadth of innovation. Clever hacks built the industry.
To be clear, although the proposal was “returned for revision,” it is essentially accepted. It’s just the syntax that’s being debated now (e.g. changing subclassable
to open
).
Update (2016-07-18): Friedrich Markgraf:
Same issue with Apple’s removal of Xcode plug-ins it prevents unanticipated innovation
Another nail in the coffin. Swift: “No programmers except myself should be trusted”
If objective-c had been like this we would have had only default coloured navigation bars until ios4.
I’m… not keen on the idea of final by default.
Do not want.
This is why you don’t let compiler engineers control the fate of your computing platform(s)
This feels like a real step against getting things done in Swift
I’m in two minds. It won’t impact existing Cocoa APIs as they’re all ObjC, & future Swifty APIs will use fewer classes anyway.
But values are never the stuff in need of hacking, it’s the classes that do complex stuff.
This “claimed bugs” language makes me uncomfortable.
I’ve been worried that guys designing languages/frameworks don’t maintain GUI apps using them, or they’d know there are bugs.
I’ve been fighting with lack of dynamism on Android. When you find issues in the OS you have to rewrite the whole class
My outsider’s perspective of Swift: X can be misused, so we’re banning X.
Seems like coding with safety scissors.
It seems like a lot of recent decisions are justified “it’s no so bad because you can just go and ask the vendor to fix it”.
yes Radar is a fantastic bidirectional communication tool with a responsive vendor, what could go wrong?
if Apple knows there’s no workaround for their bugs, maybe they’ll get better at shipping quality software.
Well, apparently the core team never had to write software against Apple frameworks. They have no idea how buggy they are.
They just bring their stuff over to Craig’s house if it doesn’t work. They have a mechanism for fixes; we don’t.
This will raise the cost of developing software, and make bugs last longer. That’s the issue at stake here.
Or have you ever been at the mercy of a non-active / committed library author? :)
A lot of cool things I built for the community or in my own apps would be impossible to do if this happens.
+ Encourages more thoughtful API design
+ Encourages community-driven changes to frameworks that can benefit everyone
:( re Apple’s stance on the Swift final-by-default proposal.
“I think so, Brain, but don’t people need to ship software without waiting years for us to fix the bugs they encounter?”
for final in swift, is there no backdoor option? ’cuz if it’s truly enforced, that’s super painful and short-sighted
I think this is one of the worst decisions in the history of language design.
I disagree that an
open
method overridden from a superclass is implicitlyopen
.As the rationale for the proposal states, overridability is hard to get right, and there is no guarantee that the consumer of an API is going to think about it. The default for override methods should not be
open
orfinal
, it should be the internal equivalent.
I don’t think that defaulting to non-open would be a good idea. Like I covered in the proposal, inherited open methods remain open; letting an override implicitly close off an open method would create a pretty unfortunate error-of-omission situation.
We could remove the default here and require the method to be explicitly open/nonopen/final, but then we really do pile up the modifiers[…]
So simply as a matter of following the logic of the proposal, every subsequent extension of something explicitely marked as open for subclassing should defacto be placed back into the default non-subclassable state, forcing the author to have to explicitely validate the new set of relationships existing between the methods in the subclass. Otherwise the logic does not hold, and the original proposal does not hold water[…]
I’m enthusiastic about sealed-by-default classes, but to be honest, I personally have no idea what default (if any) would be best for class members. Ask me again after I’ve worked with the new classes for a couple of months.
This is what i find so unique about this situation: there is not 2 months to decide, there is not a couple of implementations to compare.. there is here and now to decide for the next 20 years, with zero experience with the api and very little external references (which nobody seems to [have] any practical experience with) ... nonetheless it must all be fleshed out before the 28th. I [sincerely] hope the core team is more prepared than they currently let out.
- infinity for this proposal.
If adopted, it will mean that whenever I want to extend a class implemented by a short-sighted developer, I’ll have to resort to wrappers and classes that own instances of the lobotomized class, etc, etc.
Users who are third-party framework devs I expect will benefit the most.
They don’t have Apple’s resources to deal with all the backward-compat testing to cope with unconstrained subclassing and patching.
I would really like to see the methods match the open-ness or final-ity of their enclosing scope by default. Note: I also feel this way about access levels, which work this way except for public… I believe they should work the same way for public too (while keeping internal as the default level for top-level structures). If a class is public open, I am typically going to want 85%+ of the methods to also be
public open
. Having to annotate each of them adds a lot of visual noise, and is fairly easy to forget to add when refactoring amidst that visual noise. I already have this problem withpublic
, so I expect it will only be worse forpublic open
.
Update (2016-07-19): @Gary_BBGames:
I like Swift, but this will be an absolute shit sandwich
The reasoning from the core team that working with vendors replaces the ability to subclass to work around problems simply doesn’t hold water for me. This is a very real and very common issue, and there are endless cases where vendors won’t or even can’t solve the problem, especially in closed-source code.
[…]
In short, saying “filing a bug will work” isn’t good enough to justify locking out the ability of developers to deal with problems in vendor code. It’s simply not true - it’s certainly almost never been true of Apple frameworks, and even when it has, that doesn’t help anyone “now”. (To be clear, I’m not suggesting Apple is unresponsive to Radars. However, I am saying that there’s no transparency, no confidence in getting fixes, and no hope of any kind of reasonable (from a local perspective) timeline for deployment of fixes.)
[…]
If not for the practical considerations, I’d love it! Conceptually speaking, I find it elegant, even harmonious. But again, in practice, the result isn’t so pretty.
-1, I really think this a step in the wrong direction.
I recognise the problem around this, and why something is needed. However, I don’t like the idea of restricting things by default. In the attempt to solve a problem, we will create more problems by introducing more workarounds to replace what now is being overridden.
You know, one thing I haven’t seen mentioned is that, just as sealed-by-default preserves the options of library programmers, it also preserves the options of the language itself.
Suppose the people who think this is a huge mistake are correct, and we ultimately conclude that sealed-by-default is a disaster. What can we do about it? Well, we change Swift 3+n to open all classes by default. This would be source- and binary-compatible with all existing code; the only wrinkle is that classes compiled with a sealed-by-default compiler would still be sealed. (And that’s not even a problem yet, since stable ABIs are not a thing yet.)
The reverse, however, is not true. Going from open-by-default to sealed-by-default is source- and binary-incompatible. If we don’t do it now, we may never be able to do it.
In reality, nobody will measure the effect of this change, and those in favor of the proposal might even call the absent of a horrible catastrophe a “proof” for their opinion.
As an API author, if I need to have a value type, I cannot use inheritance. And more important, especially with regards to many of the arguments against this proposal, you cannot fix any issues with these struct types with inheritance either. It’s at this point that I find your arguments extremely weak: if it is so crucial that inheritance be enabled by default for class types, why are you not more concerned about Swift’s focus on value types and using value semantics for APIs? After all, there are pretty much no APIs in Swift’s libraries that you will be able to patch this way.
I am, and I’ve been writing about that concern from the beginning. As I said at the top, this proposal is just one piece of the larger trend.
There’s absolutely nothing in this proposal that prevents Swift from providing tools to get you access to what you need. For example, imagine a world where you could download a developer Swift module that contains all of the unoptimized code.
This is the hypothetical “escape hatch” that people have been talking about. In theory it could work, if implemented, and if you were able to get all the unoptimized code. That seems rather unlikely to me, and it would be a lot of trouble to go through for the (I think) small benefits the proposal would provide. There are so many more pressing issues I would like the Swift team to work on than building restrictions and then an escape hatch for them.
Update (2016-07-21): Benjamin Mayo:
In general, language design should not be decided by the possible existence of buggy code. However much we strive to make perfect code, there will always be bugs.
Eli Perkins (via Natasha Murashev):
I would have loved to seen this implemented as an opt-in compiler-time flag, rather than a syntactical level keyword.
It seems like for 3rd party libraries only, we are afraid that people will subclass things when they shouldn’t? Is this a real problem? I’ve honestly never seen anyone incorrectly subclass something in a 3rd party library when there was a good alternative. Maybe I have and I forget, but it happens so rarely that I don’t think it’s a real problem to fix.
With this proposal: consumers are screwed until the library is fixed, but the change is backwards compatible.
Without this proposal: consumers are fine to choose what to do (and maybe make mistakes), but the change could be backwards incompatible.
To me, consumers not being screwed > changes being backwards compatible.
I have no concerns intrinsically to the behavior discussed but rather to the complexity that this brings. Creating another type of access control and possibly keyword just adds complexity to an already fairly complex language.
I am reminded of the
atomic
/nonatomic
situation in Objective-C. Yes,atomic
is generally safer, but almost every property in almost every class is declarednonatomic
. It was a mistake to setatomic
as the default, which caused a profusion of noise in property declarations.
The proposal has been returned for revision, again. :-)
As with the first round of discussion, the community generated a large number of emails, exploring the various aspects of the proposal. While many community members agree with the thrust of the proposal, a number of people are concerned with the boilerplate being introduced by the proposal, among other issues. The core team spent over two and a half hours discussing this topic from first principles, and has come up with a similar-but-different approach that should reduce the boilerplate, while still accomplishing the primary aims of the proposal. John McCall will be revising the proposal today and we’ll restart a short discussion period about it tomorrow.
Karl:
Conflation of
public
andopen
; it feels like open is a new higher access level, like getting married, or going sudo or something. If this proposal was accepted,open
should substitutepublic
, and never be alongside it (the same waypublic private class
makes no sense)
In the original proposal (and the ensuing discussion), there was tacit agreement that subclassability/overridability and access levels should be orthogonal. However, given the direction that the design has taken since then, I think we should revisit that decision.
[…]
Third, developers already understand access levels and how they interact. If open is just an access level, all of this proposal’s changes can be fully and naturally described in one line: “public no longer includes the right to subclass or override. To get the behavior formerly known as public, use open instead.” Clear, concise, and not very controversial.
Update (2016-07-23): See also: Core Intuition.
Update (2016-07-25): Scott James Remnant:
I have no objection to the fundamental concept of the proposal, but this should not come at the cost of the language, just on the grounds of the timeline. I think it would be better for this to be re-proposed without the rush for future versions of Swift - especially since it’s “additive.”
I’ve been reading mailing list for the last two reviews of this proposal and discussion turned from “We shouldn’t add this at all” - which was kind of justified to “How actually will it interops with other swift features” - which still has a lot of questions of how exactly this modifiers will play out with other accessibility modifiers, and it seems like there is still no single answer. As Scott said, I have the same feeling that this proposal is being rushed to be accepted before the changed to Swift 3 are locked, and this won’t do any good to its semantics.
This is one of the most contentious topics I’ve seen come across Swift so far. While there may be no stopping the avalanche now as far as implementing the proposal in general (which I remain against, though many of the arguments I’ve heard in favor of it are starting to shake my conviction!), it definitely begs more consideration than running up hard against a release deadline. Goes double considering the disagreement even among the proposal’s supporters about the best semantics.
Update (2016-07-29): Chris Lattner:
The third review of “SE-0177: Allow distinguishing between public access and public overridability” ran from Active review July 21...25. The proposal has been accepted with revisions.
This proposal was far better received by the community than previous versions of the proposal, and the “first design” was the favored path within it. However, there were some concerns raised about the complexity of the model, stemming from non-obvious combinations like “open private”. As such, the core team has requested that the proposal be revised to make “open” function as another access control specifier. “open” is now simply “more public than public”, providing a very simple and clean model.
John has already revised the proposal to the new model, I encourage you to read it if you haven’t already.
Here’s the final version of the proposal.