+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

Reply via email to