I was on the fence initially, but you've sold me here. +1 for sealed-by- default.
-Kevin Ballard On Sat, Dec 19, 2015, at 07:21 PM, Jordan Rose via swift-evolution wrote: > >> On Dec 7, 2015, at 20:30 , John McCall via swift-evolution <swift- >> [email protected]> wrote: >> >>> On Dec 7, 2015, at 7:18 PM, Matthew Johnson via swift-evolution <swift- >>> [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
