[swift-evolution] [Pitch] consistent public access modifiers

Rien Rien at Balancingrock.nl
Wed Feb 15 12:57:50 CST 2017


> 
> On 15 Feb 2017, at 19:27, Adrian Zubarev <adrian.zubarev at devandartist.com> wrote:
> 
> Inline:
> 
> -- 
> Adrian Zubarev
> Sent with Airmail
> 
> Am 15. Februar 2017 um 19:11:12, Rien (rien at balancingrock.nl) schrieb:
> 
>> 
>> > On 15 Feb 2017, at 18:09, Adrian Zubarev <adrian.zubarev at devandartist.com> wrote: 
>> > 
>> > As I already said: 
>> > 
>> > • To make that feature happen, we need the protocol to be public (regardless if you can conform to it or not). 
>> > • Today you can conform to every protocol because in reality they are open today. 
>> > • If we remove the property requirement from the protocol the client can conform it to any type and break my API by calling the subscript with a wrong type: document["something", NSObject()] (assuming extension NSObject : SubscriptParameterType). 
>> > • That forces me to create a requirement for the protocol which solves the issue in my cases, however there might exist other issues, which could be far more complicated than mine is. 
>> > • That also implies I have make the enum public. 
>> > • Which follows by the fact that I’m creating unnecessary copy operations to wrap every instance that conforms to my protocol into an enum case. From internal API perspective I also have to unwrap the enum case. 
>> > Instead if we had consistent public vs. open behavior, I could make the protocol public (but-not-open), remove the requirement from it completely, remove the enum completely and simply cast to Int or String because as the author of the library I would know that the client won’t be able to conform to my protocol. 
>> > 
>> > Importing my library will show the client only this: 
>> > 
>> > extension Int : SubscriptParameterType {} 
>> > extension String : SubscriptParameterType {} 
>> > 
>> > 
>> 
>> Ok, thanks, I think I get it now. So you showed the work-around rather than the problem :-) 
>> However you could also implement this by creating two subscript operations, one for integer and one for string. 
> No not really, the idea behind this approach is carefully crafted. Plus I do not want optional chaining between subscripts, this is messy `document[“string”]?[10]?[“key”]`.

Yes, but there are always more ways to skin a cat :-)
In my SwifterJSON I do use both an int and a string subscript and have solved the optional chaining (rather ugly… but it works).
Also, I overloaded the pipe (“|”) operator that allows me to do away with the array notation like this: … = json | ”books” | 3 | “title"


> 
>> 
>> 
>> So while I agree that this might serve as an example on why a non-confirmable protocol might be useful, my original question still stands: how can subclassing create “brittle” libraries? 
> If one would want the client user only to use subclasses of a certain superclass of the same library for whatever reasons. These subclasses might interact with each other internally (which is not meant to be public by any means). `final public` will not make it happen because then you couldn’t create any of these subclasses at all.
> 
> You’re asking here such a general question that simply cannot be answered easily. I also could ask everyone why on earth would be want to hide anything from the client? Lets make everything public, if the client breaks something it’s his fault. <— Clearly not the way to go right? A distinction between `public` and `open` adds more flexibility to solve issues that previously might not had any good solutions.

It does, I am not doubting that at all.
But there are always costs, adding an extra keyword and an extra access layer adds complexity, adds possibilities for errors, leads to more testing, increases cognitive load, steepens the learning curve etc etc. And it does so for ALL swift users. Possibly far outweighing the benefits that accrue to (maybe) just a few developers.

(Like the infamous tax problem: people won’t spend much effort to change the tax system and pay $1 a year less in taxes, but the one organisation/person receiving all those dollars has a huge incentive and will spend -if necessary- all his time to keep the system as is)

> 
>> 
>> 
>> Also note that adding an extra access level did nothing to prevent brittle libraries… that task is still with the developer. 
>> 
>> PS, this is not an educational list, so … :-) 
> Being sarcastic? 

No, not really. I just wanted to draw attention to the fact that there is no obligation whatsoever to teach me.
So thank you for taking the time!

In your debt,
Rien.

> 
>> 
>> 
>> Regards, 
>> Rien. 
>> 
>> > 
>> > 
>> > -- 
>> > Adrian Zubarev 
>> > Sent with Airmail 
>> > 
>> > Am 15. Februar 2017 um 17:50:41, Rien (rien at balancingrock.nl) schrieb: 
>> > 
>> >> 
>> >> > On 15 Feb 2017, at 17:22, Adrian Zubarev via swift-evolution <swift-evolution at swift.org> wrote: 
>> >> >  
>> >> > A short example where I personally wanted a public-but-not-open protocol: 
>> >> >  
>> >> > public protocol SubscriptParameterType { 
>> >> >  
>> >> > // This property was needed to prevent the client from breaking 
>> >> > // the library by conforming to the protocol, but I'd like to  
>> >> > // keep it invisible for the client, or even better prevent the 
>> >> > // client from conforming to the protocol. 
>> >> > var parameter: Document.SubscriptParameter { get } 
>> >> > } 
>> >> >  
>> >> > extension Document { 
>> >> >  
>> >> > public enum SubscriptParameter { 
>> >> >  
>> >> > case string(String) 
>> >> > case integer(Int) 
>> >> > } 
>> >> > } 
>> >> >  
>> >> > extension String : SubscriptParameterType { 
>> >> >  
>> >> > public var parameter: Document.SubscriptParameter { 
>> >> >  
>> >> > return .string(self) 
>> >> > } 
>> >> > } 
>> >> >  
>> >> > extension Int : SubscriptParameterType { 
>> >> >  
>> >> > public var parameter: Document.SubscriptParameter { 
>> >> >  
>> >> > return .integer(self) 
>> >> > } 
>> >> > } 
>> >> >  
>> >> > // Somewhere inside the `Document` type 
>> >> > public subscript(firstKey: String, parameters: SubscriptParameterType...) -> Value? { … } 
>> >> >  
>> >> > That implementation enables more safe queries of my Document type like document["key1", intIndexInstance, stringKeyInstance, 10, "key"] rather than document["key1/\(intIndexInstance)/\(stringKeyInstance)/10/key”] 
>> >> 
>> >> I see how that makes queries better. 
>> >> However what I do not see is how making the protocol “open” would make this less safe. 
>> >> (I do not see a reason to make it open either, but that is not the question) 
>> >> 
>> >> It may be obvious to everyone else, but I don’t see it. Am I suffering from a brain freeze?. 
>> >> 
>> >> Regards, 
>> >> Rien. 
>> >> 
>> >> 
>> >> > . 
>> >> >  
>> >> 
>> >> >  
>> >> >  
>> >> >  
>> >> > --  
>> >> > Adrian Zubarev 
>> >> > Sent with Airmail 
>> >> >  
>> >> > Am 15. Februar 2017 um 17:03:32, Matthew Johnson via swift-evolution (swift-evolution at swift.org) schrieb: 
>> >> >  
>> >> >>  
>> >> >>> On Feb 15, 2017, at 9:59 AM, Rien <Rien at Balancingrock.nl> wrote: 
>> >> >>>  
>> >> >>>>  
>> >> >>>> On 15 Feb 2017, at 16:45, Matthew Johnson <matthew at anandabits.com> wrote: 
>> >> >>>>  
>> >> >>>>>  
>> >> >>>>> On Feb 15, 2017, at 9:35 AM, Rien <Rien at Balancingrock.nl> wrote: 
>> >> >>>>>  
>> >> >>>>>  
>> >> >>>>>> On 15 Feb 2017, at 16:11, Matthew Johnson via swift-evolution <swift-evolution at swift.org> wrote: 
>> >> >>>>>>  
>> >> >>>>>>  
>> >> >>>>>>> On Feb 15, 2017, at 5:59 AM, Jeremy Pereira via swift-evolution <swift-evolution at swift.org> wrote: 
>> >> >>>>>>>  
>> >> >>>>>>>  
>> >> >>>>>>>> On 15 Feb 2017, at 11:11, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote: 
>> >> >>>>>>>>  
>> >> >>>>>>>>  
>> >> >>>>>>>> Our philosophy in general, however, is to default to the behavior which preserves the most flexibility for the library designer. 
>> >> >>>>>>>  
>> >> >>>>>>> Actually, I thought the philosophy was to preserver type safety. When did that change? 
>> >> >>>>>>>  
>> >> >>>>>>> Also, when was the library designer prioritised ahead of the application developer? 
>> >> >>>>>>>  
>> >> >>>>>>>  
>> >> >>>>>>>> Both open and non-open classes are common, but we chose to give non-open classes the `public` keyword because that's the flexibility-preserving option. 
>> >> >>>>>>>  
>> >> >>>>>>> No it isn’t, it’s the flexibility restricting option. The consumer of an open class can subclass it. The consumer of a public class cannot subclass it. How is the second more flexible than the first? 
>> >> >>>>>>  
>> >> >>>>>> It reduces complexity for the library author by allowing them to opt-out of the complexity involved in supporting unknown, user-defined subclasses. It is important to allow libraries to have this flexibility. They are free to declare a class `open` if they want to allow subclassing. It’s even possibly for a library to declare all classes `open` if it wishes to do so. But *requiring* that would reduce the design space libraries are allowed to explore and / or introduce fragility by moving the subclass restriction to a comment. 
>> >> >>>>>>  
>> >> >>>>>  
>> >> >>>>> Why would a library author want to prohibit subclasses? 
>> >> >>>>> A library user can always wrap the class and subclass the wrapper. 
>> >> >>>>  
>> >> >>>> This is composition, not inheritance. The most important difference is that a wrapper cannot override methods, it can only wrap and / or forward them. This means that when the superclass calls a method on `self` that method *always* invokes its version of that method rather than a subclass override. This is a very important difference. 
>> >> >>>>  
>> >> >>>  
>> >> >>> Agreed, however that does not answer the question why would a library developer want to disallow subclassing? 
>> >> >>> I do not see a use case for that. I.e. a feature that cannot be implemented without it. (without “open”) 
>> >> >>  
>> >> >> The feature it enables is more robust libraries and the ability for library authors to better reason about their code. You may not find this benefit enough to be worth a language feature, but many of us do. 
>> >> >>  
>> >> >>>  
>> >> >>> Rien. 
>> >> >>>  
>> >> >>>>>  
>> >> >>>>> There are cases where subclassing does not make sense. And thus preventing subclasses adds information for those users that don’t RTFM. But that imo is not worth the impact extra complexity places on all other users. 
>> >> >>>>>  
>> >> >>>>> Rien. 
>> >> >>>>>  
>> >> >>>>>>>  
>> >> >>>>>>>  
>> >> >>>>>>> _______________________________________________ 
>> >> >>>>>>> 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 
>> >> >>  
>> >> >> _______________________________________________ 
>> >> >> 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 
>> >>



More information about the swift-evolution mailing list