[swift-evolution] access control

Ilya Belenkiy ilya.belenkiy at gmail.com
Sun Jan 24 05:10:07 CST 2016


The main idea is that “local” makes the author’s intent clear (the function or property should not be visible outside the scope) and it’s enforceable by the compiler and protects against accidental misuse. It also makes deliberate misuse easier to spot because it would require changes to the function or property declaration.

It is different from private in that you can add code to the same file and access anything marked as private and break things by calling something that was not supposed to be used directly. With local, you are forced to modify the function declaration, and you know that you are using the function or a property that was specifically hidden by the author. With private, you can never know if it can or cannot be called in the same file without the danger of breaking class invariant.

I think that lexical scope (type based) access would be enough. I wouldn’t want to require self (C++ and C# model) because it’s about the type, not the instance. But if this was the only way to get this accepted, I’d support that. It would be much better than nothing.

> Where is the appropriate floor on access restrictions? Why isn't a single file the appropriate floor?

I think that it’s scope. It’s the smallest subdivision that can exit, and it provides a way to specify a public API for a class. Private as it is today does not express public / private API for a class. It only expresses access inside the same compilation unit, which is different.

> On Jan 23, 2016, at 10:50 PM, Curt Clifton <curt at omnigroup.com> wrote:
> 
> Thanks for sharing that.
> 
> It seems like the proposal would be stronger with some specific code examples of problems that can arise with `private` that are ameliorated with the introduction of `local`. I get the basic idea: with `private` I have to understand all the invariants in the file while with `local` I only need to understand the invariants within the lexical scope. However, I'm having trouble understanding the situation where those are sufficiently different to justify increasing the complexity of the language. (If you were proposing replacing `private` with `local`, then the complexity would be the same. Of course, in that case, you'd need to justify removing the level of access that allows extensions to access private members.) I may just be failing to understand. If so, a sufficiently detailed example would help, I think.
> 
> Secondly, in some languages, private members can only be accessed on self instances. Is your intention that local members can only be accessed thusly. That is, would the following be allowed:
> 
> struct Foo {
>  local var bar: String
>  // ...
>  mutating func concatBar(otherFoo: Foo) {
>    let othersBar = otherFoo.bar // legal?
>    bar = bar + othersBar
>  }
> }
> 
> More generally, different languages have provided a wide variation in the specificity of their access control mechanisms. If `local` is only about static scope (i.e., the example above is legal), then do we need a mechanism for restricting to self-only access? Where is the appropriate floor on access restrictions? Why isn't a single file the appropriate floor?
> 
> Cheers,
> 
> Curt
> 
> 
>> On Jan 23, 2016, at 6:37 PM, Ilya Belenkiy <ilya.belenkiy at gmail.com> wrote:
>> 
>> Here is a link to the pull request:
>> 
>> https://github.com/apple/swift-evolution/pull/64
>> 
>> 
>>> On Jan 23, 2016, at 9:23 PM, Curt Clifton <curt at omnigroup.com> wrote:
>>> 
>>> It might aid the discussion if you provided a link to your proposal. I'm assuming it includes examples of problems that your proposed changes solve. I'd be interested in seeing those.
>>> 
>>> Cheers, 
>>> 
>>> Curt
>>> ------------------------- 
>>> Curt Clifton, PhD 
>>> Software Developer 
>>> The Omni Group 
>>> www.curtclifton.net 
>>> 
>>> 
>>> On Jan 23, 2016, at 6:17 PM, Ilya Belenkiy via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>>>>> Yes, but with an API based access control,
>>>>> 
>>>>> I don't know what that term means, sorry.
>>>> 
>>>> I had to come up with this term to distinguish it from a file based access control. In API based access control, visibility of anything is tied to the declaration instead of the file it is in. It’s the model that C++ and C# ( and I am sure many others) use. In file based access control, visibility of anything is tied to the file it is in and has little to do with the class API itself. It’s the model that Swift currently follows.
>>>> 
>>>>>> anyone modifying / extending the class can instantly see the author’s
>>>>>> intent from the API declaration,
>>>>> 
>>>>> I don't see how that's different from what we have in Swift.  Private
>>>>> APIs are the ones you don't want anyone to touch because they can break
>>>>> your invariants.
>>>> 
>>>> In Swift, any additional code in the same source file can use the APIs declared private and break your invariants. The semantic meaning of private is not “use only inside the class” but “use only inside this file”. It is currently impossible to express “use only inside the class” semantics.
>>>> 
>>>>> Seeing the keyword “private” isn't enough for you in Swift, but it is in
>>>>> C++?  Why?
>>>> 
>>>> Because in Swift, private means “use only inside this file” and not “use only inside this class / scope”. In C++ private means “use only inside this class”. Moreover, C++ puts a special emphasis on this — variables are private inside the class unless the code specifically says otherwise.
>>>> 
>>>>> Defining one type per file is usually a good practice anyway.
>>>> 
>>>> This really depends on the context. There is value in keeping similar concepts / implementations in the same file. If one class and one extension per file become a requirement, then private will have the same semantics as in C++. It will also become very inconvenient to write code because even a small extension with 1 method that can fit on one line will require a separate file.
>>>> 
>>>>> Without prohibiting class extensions from being made in the same file
>>>>> where the class is defined—a capability we want to keep available to
>>>>> class authors—that will always be the case.
>>>> 
>>>> 
>>>> It doesn’t have to be. My proposal (still a pull request) provides a way to explicitly declare a function or a property to be private to the scope where it is defined. So extensions and subclasses declared in the same file cannot access it and break the invariant. If it’s applied to both class definitions and extensions, it’s very consistent and provides an explicit declaration of intent that can (and should) be enforced by the compiler.
>>>> 
>>>>> Therefore, you have the
>>>>> same scenario as in C++: anyone who modifies the file where a class is
>>>>> defined might be violating its invariants.  It's just that in C++ the
>>>>> violator has to write code between a particular set of braces.  When
>>>>> reviewing diffs it's very common not to have enough context to see those
>>>>> braces anyway.
>>>> 
>>>> This is a crucial distinction. That particular set of braces defines a scope which acts as a black box and hides the implementation details. Anything outside the scope cannot damage the internal state, and the compiler *enforces* it. In Swift, the compiler only enforces that the API is not visible from another file because the language has no way to express “this should be visible only in the scope to hide implementation details”. The only exception to that are functions inside other functions and local variables — right now this is the only way to completely hide implementation details, but it’s very limited.
>>>> 
>>>> My main 2 points are that it’s impossible to express the intent of a truly local access level to hide implementation details, and because of that, the compiler cannot enforce this intent.
>>>> 
>>>>> When
>>>>> reviewing diffs it's very common not to have enough context to see those
>>>>> braces anyway.
>>>> 
>>>> This depends on how thoroughly the code is reviewed. But the main point there is that when you find out, you know that whoever worked around a private API did so knowing that he was introducing a hack. Because Swift has no way of expressing “this is private to the class, not just file it’s in”, someone may not know and make a mistake, and the compiler cannot catch it. My proposal makes the intent expressible in the language and enforceable by the compiler.
>>>> 
>>>>> Not at all in the same way.  In C++, you can't add methods to a class
>>>>> outside the file where it's declared unless there's some really horrible
>>>>> preprocessor stuff going on, and even then the class author would have
>>>>> had to explicitly left that door open for you.
>>>> 
>>>> True, but my proposal deals with extensions the same way it does with class definitions — it provides a way to hide implementation details in a scope (whether it be a class definition or an extension scope) and make them invisible to everything else including extensions or other extensions or anything else in the same file. It provides a way to express the intent in the language and enforce it by the compiler.
>>>> 
>>>>> On Jan 23, 2016, at 5:43 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>>>>> 
>>>>> 
>>>>> on Sat Jan 23 2016, Ilya Belenkiy <ilya.belenkiy-AT-gmail.com> wrote:
>>>>> 
>>>>>>> Anyone who can "add more code to the same file" can just as easily modify/extend the class.  
>>>>>> 
>>>>>> Yes, but with an API based access control,
>>>>> 
>>>>> I don't know what that term means, sorry.
>>>>> 
>>>>>> anyone modifying / extending the class can instantly see the author’s
>>>>>> intent from the API declaration,
>>>>> 
>>>>> I don't see how that's different from what we have in Swift.  Private
>>>>> APIs are the ones you don't want anyone to touch because they can break
>>>>> your invariants.
>>>>> 
>>>>>> and the compiler can prevent *accidental* misuse. Deliberate misuse
>>>>>> cannot be stopped no matter what access control is in place (as long
>>>>>> as the coder can modify the code in the module). But at least with an
>>>>>> API based access control, a deliberate misuse is very easy to spot
>>>>>> when debugging / reviewing the code. With a file based access control,
>>>>>> the only way to tell if an API is really private or can be safely used
>>>>>> anywhere in the same file is to read the comments / source code.
>>>>> 
>>>>> Seeing the keyword “private” isn't enough for you in Swift, but it is in
>>>>> C++?  Why?
>>>>> 
>>>>>> And it’s much easier to miss in maintenance, debugging, or code
>>>>>> reviews. The only way to make it obvious in the current model is to
>>>>>> adopt a convention like _ in front of methods.
>>>>> 
>>>>> The only reason we're doing that in the standard library is that we're
>>>>> currently forced to make some implementation details formally public,
>>>>> and we can't use “private” because <reasons>.  If it weren't for these
>>>>> limitations, we'd be very happy with “private.”  Defining one type per
>>>>> file is usually a good practice anyway.
>>>>> 
>>>>>>> There's nothing about extending a class that ought to raise a red
>>>>>>> flag in Swift, because it's perfectly legit to do so and use only
>>>>>>> the class' public APIs.
>>>>>> 
>>>>>> Unless the programmer extends a class in the same file. Then he can
>>>>>> call anything marked private, and it’s impossible to tell without
>>>>>> reading the code whether it violates the class invariants.
>>>>> 
>>>>> Without prohibiting class extensions from being made in the same file
>>>>> where the class is defined—a capability we want to keep available to
>>>>> class authors—that will always be the case.  Therefore, you have the
>>>>> same scenario as in C++: anyone who modifies the file where a class is
>>>>> defined might be violating its invariants.  It's just that in C++ the
>>>>> violator has to write code between a particular set of braces.  When
>>>>> reviewing diffs it's very common not to have enough context to see those
>>>>> braces anyway.
>>>>> 
>>>>>> There is no way to express this in Swift today. My proposal for local
>>>>>> / scoped access control would solve this.
>>>>>> 
>>>>>>> we didn't do file-level access control because it was easier; we
>>>>>>> did it because we thought it was a better model for Swift, where
>>>>>>> types are liberally open for extension.
>>>>>> 
>>>>>> That’s what I assumed until I saw someone say it in this list when
>>>>>> someone else raised a similar concern. Types in C++ and C# are also
>>>>>> liberally open for extension,
>>>>> 
>>>>> Not at all in the same way.  In C++, you can't add methods to a class
>>>>> outside the file where it's declared unless there's some really horrible
>>>>> preprocessor stuff going on, and even then the class author would have
>>>>> had to explicitly left that door open for you.
>>>>> 
>>>>> 
>>>>> -- 
>>>>> -Dave
>>>> 
>>>> _______________________________________________
>>>> 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