[swift-evolution] access control

Ilya Belenkiy ilya.belenkiy at gmail.com
Sun Jan 24 14:15:45 CST 2016


> I think it's possible to believe that your feature would be useful
> without also believing there's something fundamentally wrong with the
> current model, and your arguments make it sound to me like you think
> you're getting a lot more certainty from C++'s "private" than you
> actually are.


Data encapsulation is a cornerstone of object oriented programming. Swift has classes, and yet it is impossible to completely hide state / implementation details inside the class. Moreover, it’s impossible to express that something is intended to be completely hidden. Since such a fundamental idea is not supported, there is something fundamentally wrong with the current model. Swift is already a great language, and this issue aside, it’s by far the best language I’ve used. In all other areas Swift does a great job to help the programmer write correct code. This is one weird and very noticeable deviation.

> We do that in Swift as well; we just chose a different box boundary.

The box boundary is not arbitrary. The current model is perfect for code without classes. But for classes, data encapsulation is not just a “nice to have” feature. It’s such a fundamental part of object oriented programming that it’s expected. This would be like having no functions in a functional language or no variables in a procedural language :–)

> Whoa, I never said anything about one extension per file, or about
> requirements.

This would be the only way to make the current meaning of private perfectly match what everybody outside of Swift expects it to mean.

> That is true.  The question is, would having a way to express that be
> worth the complexity it introduces?  Reasonable people can disagree
> about that.  

I think that it’s worth every bit. I can live without “protected”. It can be argued that once you expose something, you lose control over it. But “private” is different. I’ll reference data encapsulation again. I don’t understand how anyone can seriously argue that it’s not important in a language that aims to support object oriented programming.

> Usually when they have an intimate relationship and need access to one
> another's implementation details.  We didn't want to end up with
> "friend" as in C++, so we made file-level access control what you get
> from "private.”

Yes, I totally agree that having a way to say “available to all in this file” is useful precisely for this reason. I just think that it’s misnamed. The honest way to call it would be something like “file internal”. But even if the right name is already taken, we can still have the access level itself. I proposed “local” or “scoped”. (I’d prefer to rename private to file internal and call local “private”, but that raises backward compatibility issues, and I didn’t want the proposal to be rejected just for that reason.)

> For what it's worth, my background, up until a few years ago, is as a
> C++ programmer.  I was a little uncomfortable at first with the idea of
> giving up a "limit-to-the-type" level of access control, but when I
> considered how real C++ code was developed I realized that people
> generally control changes to code at file-level granularity.  If they
> want protection from themselves, they can choose the file boundaries
> accordingly.  IMO it's a good trade to avoid having a wart like "friend"
> in the language.  Another level of access control might be useful, but
> I don't think it's crucial.

This may be true, but the right solution is not do abandon data encapsulation. It’s to make it easy to use correctly. I always used const in C++ wherever possible, but it was a pain. Swift implementation is perfect. Very easy to use and encourages good practice. I don’t see why the same couldn’t be done with access control. (I think that making “local” the default access level would be a perfect solution, but simply having the option would be good enough for me. I’d use it everywhere to hide internal state and implementation details.)

> On Jan 23, 2016, at 10:01 PM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 
> on Sat Jan 23 2016, Ilya Belenkiy <swift-evolution at swift.org <mailto: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.
> 
> I disagree with your analysis.  Every declaration in Swift has a
> visibility.  That visibility is attached to the declaration itself, not
> to the file.  Every declaration can live in only one spot, and that's
> where the visibility is set.  You can't go to another file and do
> something there to alter the declaration's visibility.  The fact that we
> made the most restrictive visibilty level "visibile within the file"
> rather than "visible within the type" does not mean the model is
> fundamentally different.
> 
>>>> 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.
> 
> That is true.  The question is, would having a way to express that be
> worth the complexity it introduces?  Reasonable people can disagree
> about that.  
> 
>>> 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.
> 
> I ended up generally being explicit about scoping in C++ anyway: The
> most-accessible (public) part of a C++ class declaration should appear
> at the top, so you end up with everything thereafter being public unless
> you go out of your way to explicitly specify access.
> 
>>> 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. 
> 
> Usually when they have an intimate relationship and need access to one
> another's implementation details.  We didn't want to end up with
> "friend" as in C++, so we made file-level access control what you get
> from "private."
> 
> For what it's worth, my background, up until a few years ago, is as a
> C++ programmer.  I was a little uncomfortable at first with the idea of
> giving up a "limit-to-the-type" level of access control, but when I
> considered how real C++ code was developed I realized that people
> generally control changes to code at file-level granularity.  If they
> want protection from themselves, they can choose the file boundaries
> accordingly.  IMO it's a good trade to avoid having a wart like "friend"
> in the language.  Another level of access control might be useful, but
> I don't think it's crucial.
> 
>> 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.
> 
> Whoa, I never said anything about one extension per file, or about
> requirements.
> 
>>> 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. 
> 
> We do that in Swift as well; we just chose a different box boundary.
> 
>> 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.
> 
> I think you'll agree that if you ignore (or are ignorant of) the intent
> behind the application of any given access control construct, and you
> have the ability to change all the code in the project, you are likely
> to break someone's invariants.  If you aren't willing to treat something
> that's labelled as "private" as "don't touch unless you know what you're
> doing," there's nothing the compiler can do to enforce correctness, and
> there's not much difference between making make changes in the file
> where it's declared and making changes between the braces where it's
> declared.
> 
>>> 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.
> 
> I think it's possible to believe that your feature would be useful
> without also believing there's something fundamentally wrong with the
> current model, and your arguments make it sound to me like you think
> you're getting a lot more certainty from C++'s "private" than you
> actually are.
> 
> 
> -- 
> -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/20160124/ecfdeaff/attachment.html>


More information about the swift-evolution mailing list