<div dir="ltr">All of these examples for more access control could and should be refactored to have simple interfaces with completely hidden implementation details. <div><br></div><div>The discussion has become unnecessarily complicated. This proposal is about a fundamental concept of software engineering: provide a small and simple interface that serves a particular purpose and hide implementation details. </div><div><br></div><div>Implementation details need to be hidden for several reasons:</div><div>- ensure that the invariant of the public API holds true regardless of how the public API is used. For example, a stack's element count and relative order changes only with push() and pop().</div><div>- the implementation could be changed without affecting how the API is used. For example, a stack backed by an array could turn into a stack backed by a linked list.</div><div>- the API that the user has to deal with is small. If the user can see the internals, he has to constantly separate what's public (and allowed) and what is private (and could change).</div><div><br></div><div>Currently, the only 2 ways to express this concept in Swift are</div><div>1) use private and put the implementation into a separate file</div><div>2) use _ or some other convention to distinguish public APIs from the functions that are used for implementation</div><div><br></div><div>Using separate files works, but it doesn't work well for related concepts. It often makes sense to group similar concepts / interfaces / APIs in one file. This solution comes at the expense of not being able to do this, which I find extremely limiting. An analogy: it makes sense to put separate chapters of a book into different files, but a file per paragraph is extremely inconvenient. </div><div><br></div><div>Using _ also helps, but it's coding by convention. It is still possible and easy to break it and break the code. And we see it all the time with ObjC developers trying to use private APIs to fix a bug or add functionality that is not public. It also forces the programmer to sort through all the _ in search for the APIs that he can use. And if he first sees a similar private API, it takes a mental strain not to use it and keep searching for the pubic version (if there is one).</div><div><br></div><div>Using local solves the problem an all accounts:</div><div>- the compiler enforces that the invariant holds true (as long as the implementation is correct)</div><div>- the auto completion shows only the methods that are available instead of showing the APIs that the user should not use</div><div>- the code can be organized in a way that makes sense with grouping of related code into one file without introducing oversharing of APIs.</div><div><br></div><div>Local is also very much in line with Swift's focus on strong type system, elimination of accidental mistakes, and code clarity. If we have guard that can be easily replaced by an if statement, I don't see how local is a problem. If you say "but guard helps eliminate lots of nested if statements", I can reply "but local helps eliminate lots of tiny files". Any arguments about cognitive overhead, making language bigger, etc. can be argued the same way. The biggest difference though is that local is not only about clarity and convenience, it's also about correctness. I would even argue that local should be the default and that any other access level modifier should be a deliberate choice by the programmer.</div><div><br></div><div>--</div><div>Ilya Belenkiy</div><div><br></div><div><div><div class="gmail_quote"><div dir="ltr">On Sun, Dec 13, 2015 at 4:58 PM Brent Royal-Gordon via swift-evolution <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">>> I mean, look, you’re right. In your case, three access modifiers isn’t enough to prevent this mistake; you need four. But,<br>
>> given four, you could provide an example that needs five. Given five, you could come up with one for six. Given N access modifiers, you can write a case where you need N + 1.<br>
><br>
> Nope. What we're talking about (what I'm talking about, anyway) is a syntactically scoped access modifier. There is no N+1 case. There is no "even more scoped". There is no inductive case.<br>
><br>
> I mean, it is possible to write a troll proposal for an access modifier visible to the 3 lines before and after the declaration, or something. That much I grant. But troll proposals aside, a scoped access modifier is the quark of the access modifiers; there is no sensible way to go halvsies.<br>
<br>
I actually immediately thought of several things you might want:<br>
<br>
• You have two logical “groups” of properties and methods in a type, and you want to only permit access within a group. But both groups have stored properties, so they must be defined in the scope of the main type definition.<br>
• You have types nested within your type. You want methods in your own type to have access, but not methods in those nested types.<br>
• Alternatively, you have types nested within your type. You want the outer type to have access to things in the nested type, but not any other type.<br>
<br>
I don’t think these are troll proposals. They *are* a little more limited, a little more esoteric, but you could say the same thing about `local` vs. `private`. And in some cases you can emulate some of these with the four access modifiers you want, but again, you could say the same thing about `local` vs. `private`.<br>
<br>
>> Actually, for this case, there *is* a way to achieve better safety without additional access modifiers. Create a new file and call it Synchronized.swift:<br>
><br>
> This is the solution that I ship. I think if we don't add a new visibility modifier, we should definitely do this in the standard library, because "resource synchronization" is a problem most modern programs have.<br>
<br>
This is not a bad idea. The only potential issue I can see with it is that, if it’s made too general, it might be difficult to construct a Synchronized instance correctly.<br>
<br>
>> My solution is to be careful when I’m writing code.<br>
><br>
> It is actually *this* statement which is weak to an induction attack. We can *always* be more careful writing code. Why do we need typechecking, why do we need integer overflow trapping, why do we need array out of bounds exceptions? The C developer says they do not need half of Swift because their solution is to be more careful writing code. The C developer is naive.<br>
><br>
> But in *this* language, we adopt zero and low-cost abstractions in the name of safety. A scoped access modifier is a zero-cost abstraction that makes the language safer. We should adopt it. If for no other reason, than consistency with the other aspects of the language design. Either that or we should move integer overflow checks into the standard library for consistency with Synchronized, which I present as a troll proposal.<br>
<br>
But it’s not zero-cost. It’s another thing to learn, another thing to think about, another way you have to mentally analyze your code. It has to be balanced against the goal of keeping the language small and simple. And many people, when they do that, find this idea wanting.<br>
<br>
> I mean, I don't know about you, but I get called in to look at projects regularly that have a MainViewController.swift that weighs 10KLOC. I didn't write it. I didn't make the mess. I just want tools to help me clean it up. 'private' is useless in that context, and it's a (sadly) typical context in iOS.<br>
<br>
`local` is going to be equally useless in this context, because MainViewController.swift will almost certainly contain one 9.999KLOC `class` block. Cleaning up is, practically by definition, messy and tiring.<br>
<br>
--<br>
Brent Royal-Gordon<br>
Architechies<br>
<br>
_______________________________________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
</blockquote></div></div></div></div>