[swift-evolution] [swift-evolution-announce] [Review #3] SE-0117: Allow distinguishing between public access and public overridability

Karl razielim at gmail.com
Thu Jul 21 22:05:59 CDT 2016


> On 21 Jul 2016, at 22:13, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
> on Thu Jul 21 2016, Matthew Johnson <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> 
>>> On Jul 21, 2016, at 12:47 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> 
>>> on Thu Jul 21 2016, Matthew Johnson <swift-evolution at swift.org> wrote:
>>> 
>> 
>>>>> 	* What is your evaluation of the proposal?
>>>> 
>>>> +1 to the first design.  I think this is a great solution that
>>>> balances the many considerations that have been raised on all sides of
>>>> this issue.  `open` is 2 characters shorter than `public` so
>>>> complaints about boilerplate are no longer valid.  `internal` is the
>>>> “default” - neither `public` nor `open` are privileged as a “default”
>>>> for publishing API outside of a module.
>>>> 
>>>> I am interested in language enhancements such as exhaustive pattern
>>>> matching on classes and protocols which rely on knowledge of the full
>>>> class hierarchy.  Such enhancements will be far more useful if the
>>>> language supports non-open, non-final classes.
>>>> 
>>>> There are design techniques that would require additional boilerplate
>>>> if we cannot have non-open, non-final classes.
>>>> 
>>>> Most importantly, requiring library authors to choose `public` or
>>>> `open` provides important documentation value.  Users of the library
>>>> will know whether the author intends to support subclasses or not.
>>> 
>>> I think this reasoning is flawed.
>>> 
>>> If you make any methods overridable outside your module (“open”),
>>> obviously you mean to allow subclassing outside the module.  If you have
>>> no open methods, there's absolutely nothing you need to do to “support
>>> subclasses,” and from a design point-of-view, there's no reason to
>>> restrict people from subclassing.
>> 
>> I disagree.  As has been discussed when a class is not open the author
>> does not make a commitment to allow subclasses.  
> 
> Yes, but that argument is based on the *assumption* that disallowing
> subclassing is somehow an important dimension of safety or preserving
> API contracts, independent of overriding.  It isn't.
> 
> Demonstration: we don't make that argument about aggregation: you can't
> prevent someone from making your public struct a stored property in a
> type defined outside the module.  Judgements about programming style
> aside, in the absence of overriding, subclassing and aggregation are
> identical from an encapsulation point-of-view.
> 

IMO, the safety of API contracts is secondary. This is about safety of implementation - my code is possibly not "resilient" enough (to to speak) to handle overriding arbitrary public members. Not all members are equally resilient to being overridden (an implementation detail). It is not driven by ideological considerations as much as practical considerations - writing good base classes is difficult, and lots of people get it wrong, and we could improve reason-ability for all parties involved if we just marked-up what is “open” and what isn’t.

That’s also why I think it should apply to internal subclasses as well. I’m not supporting this proposal for ideological reasons, but for practical ones. And in that sense, I’m -1 (or maybe -0.5) because I don’t think it goes far enough and leaves a feeling of inconsistency as a result.

Aggregation is not a problem because the aggregate type does not have an “is” relationship with its constituents.

>> The right to make the class final is reserved for the future.  Maybe
>> this is the “nice point of control” you refer to and don’t find
>> compelling?  I would prefer to have library authors acknowledge that
>> they intend to allow subclasses and make that commitment explicit.
> 
> The question is, why?
> 
>> For me it isn’t about control as much as it is about making the API
>> contract explicit and acknowledged.  I have wondered about the intent
>> of library authors enough times to find this explicit statement in the
>> language worthwhile.
> 
> Yeah, but we don't add language features to represent every possible
> author intention, and there's a good argument that any intention that
> can be violated without harm shouldn't be enforced.

It’s the “without harm” that’s debatable. It may not cause harm in a binary sense, but it may cause harm in another sense if you pass rogue objects around which look like my own objects but can define non-conforming behaviour. The only way I can prevent that right now is with “final”, but that prevents even myself from subclassing internally.

If we went with the first option (and assuming the library-internal optimisation potential in this situation is minor), we could have a flag which lets you override the checker and subclass non-open, non-final types in 3rd party libraries. We could continue to ensure that it is safe (from a binary perspective) to subclass and let you import libraries in an “unchecked” way similar to @testable.

So if I really, really need to subclass UINavigationController, and it isn’t ‘final’, I can still do it in a file which "@unchecked imports UIKit” (or perhaps the attribute should be attached to the class declaration itself - e.g. “class FixedNavigationController : @unchecked UINavigationController” ).

While I am in favour of safe API access and reasonability, I’m not in favour of limiting flexibility where it isn’t absolutely necessary. 99% of library users will be safely living within the domains that I marked out as safe and tested, and those who need something customised can do so at their own risk.

> 
>> I also think language features enabled by knowing the whole class
>> hierarchy will provide more value than “compositional subclasses” as
>> long as we gain better support for composition elsewhere in the
>> language.
> 
> I agree that there are better ways to solve the composition/API
> forwarding problem.

+1

> 
>>> The only reasons I can see for allowing people to prevent non-final
>>> classes from being subclassed outside the module in which they are
>>> defined are:
>>> 
>>> 1. It feels like a nice point of control to have.
>>> 
>>> 2. Marginal performance gains as noted in the proposal
>>> 
>>> I personally don't find these to be convincing.  #1 in particular seems
>>> like a poor way to make language design decisions.  If we decide to add
>>> this point of control, I'll justify it to myself in terms of #2.
>>> 
>>> P.S., I can live with either alternative; it's just important to me that
>>> we understand the situation clearly when evaluating them.
>> 
>> I agree with this.
>> 
>>> 
>>> HTH,
>>> 
>>> -- 
>>> Dave
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> -- 
> Dave
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160722/ebc0cdf9/attachment.html>


More information about the swift-evolution mailing list