+1 to everything Jordan said here. Sent from my iPhone
> On Dec 19, 2015, at 7:21 PM, Jordan Rose via swift-evolution > <[email protected]> wrote: > > >>> On Dec 7, 2015, at 20:30 , John McCall via swift-evolution >>> <[email protected]> wrote: >>> >>>> On Dec 7, 2015, at 7:18 PM, Matthew Johnson via swift-evolution >>>> <[email protected]> wrote: >>>> Defaults of public sealed/final classes and final methods on a class by >>>> default are a tougher call. Either way you may have design issues go >>>> unnoticed until someone needs to subclass to get the behavior they want. >>>> So when you reach that point, should the system error on the side of rigid >>>> safety or dangerous flexibility? >>> >>> This is a nice summary of the tradeoff. I strongly prefer safety myself >>> and I believe the preference for safety fits well with the overall >>> direction of Swift. If a library author discovers a design oversight and >>> later decides they should have allowed for additional flexibility it is >>> straightforward to allow for this without breaking existing client code. >>> >>> Many of the examples cited in argument against final by default have to do >>> with working around library or framework bugs. I understand the motivation >>> to preserve this flexibility bur don't believe bug workarounds are a good >>> way to make language design decisions. I also believe use of subclasses and >>> overrides in ways the library author may not have intended to is a fragile >>> technique that is likely to eventually cause as many problems as it solves. >>> I have been programming a long time and have never run into a case where >>> this technique was the only way or even the best way to accomplish the task >>> at hand. >>> >>> One additional motivation for making final the default that has not been >>> discussed yet is the drive towards making Swift a protocol oriented >>> language. IMO protocols should be the first tool considered when dynamic >>> polymorphism is necessary. Inheritance should be reserved for cases where >>> other approaches won't work (and we should seek to reduce the number of >>> problems where that is the case). Making final the default for classes and >>> methods would provide a subtle (or maybe not so subtle) hint in this >>> direction. >>> >>> I know the Swift team at Apple put a lot of thought into the defaults in >>> Swift. I agree with most of them. Enabling subclassing and overriding by >>> default is the one case where I think a significant mistake was made. >> >> Our current intent is that public subclassing and overriding will be locked >> down by default, but internal subclassing and overriding will not be. I >> believe that this strikes the right balance, and moreover that it is >> consistent with the general language approach to code evolution, which is to >> promote “consequence-free” rapid development by: >> >> (1) avoiding artificial bookkeeping obstacles while you’re hacking up the >> initial implementation of a module, but >> >> (2) not letting that initial implementation make implicit source and binary >> compatibility promises to code outside of the module and >> >> (3) providing good language tools for incrementally building those initial >> prototype interfaces into stronger internal abstractions. >> >> All the hard limitations in the defaults are tied to the module boundary >> because we assume that it’s straightforward to fix any problems within the >> module if/when you decided you made a mistake earlier. >> >> So, okay, a class is subclassable by default, and it wasn’t really designed >> for that, and now there are subclasses in the module which are causing >> problems. As long as nobody's changed the default (which they could have >> done carelessly in either case, but are much less likely to do if it’s only >> necessary to make an external subclass), all of those subclasses will still >> be within the module, and you still have free rein to correct that initial >> design mistake. > > I think John summarized my position very well, so of course I'm going to come > in here and add more stuff. :-) > > In working on the design for library evolution support ("resilience"), we've > come across a number of cases of "should a library author be able to change > this when they release v2 of their library?" Many times, the answer is it's > possible to do something in one direction, but not at all safe to go the > other way. For example, you can always add public methods to a class, but you > can't remove public methods because you don't know who's calling them. You > can mark them deprecated, but that doesn't help with any client apps that > have already been compiled and shipped. > > One of the things that came up was "can you add 'final' to a class?" And of > course you can't, because you don't know who may have already subclassed it. > That's very unfortunate for a library author who simply forgot to add 'final' > when they were first writing the class. > > The interesting thing about this is that the "error of omission"—of failing > to think about whether a class should be final—is worse than the alternative. > Ignoring optimizations for a minute, a class that starts out 'final' can > certainly become non-final later; it doesn't change how the class is > currently used.* For a lot of library evolution questions, this is the > preferred answer: the default should be safe, and the designer of the class > can choose to be more aggressive later. > > This is also the guiding principle behind the behavior of 'public'. A number > of people have asked for members of a public struct to implicitly be made > public. But here again the "error of omission" is problematic: a helper > function you add for your own use may now be depended on by client apps far > and wide, just because you forgot to customize the access control. So Swift > says you should explicitly consider the public interface of every type. > > Why 'sealed' instead of 'final' as the default? Because inheritance is > useful, and within your own code having to opt into it starts to feel like > unnecessary clutter. This is a trade-off, just like defaulting to 'internal' > over 'private', but it's one that keeps life easy for a single developer with > a single module: their app. (And the compiler can still do useful things with > non-final classes if it can see the entire class hierarchy.) Additionally, > limiting inheritance to the current file (a la 'private') is also potentially > useful. > > This direction separates "limiting inheritance/overrides" from "has no > subclasses/overrides". The former is about defining the limits of your API; > the latter is a promise that can be used for performance. I think that's a > good thing. > > Jordan > > * Even without optimizations a 'final' class cannot safely drop the 'final'. > If a class is 'final', it may have additional 'required' initializers added > in extensions. > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
