[swift-evolution] [swift-evolution-announce] [Review] SE-0025 Scoped Access Level

Ilya Belenkiy ilya.belenkiy at gmail.com
Mon Feb 29 22:00:01 CST 2016


Most of the time, restricted access is about internal state or about helper
functions that may manipulate state in an unsafe way or make assumptions
that generally aren't true. People writing code don't think in terms of
files; they think in terms of types. "scoped" is a much clearer expression
of intent than "file" (called "private" in Swift). Putting the scope into a
separate class and applying "file" access level is a way of implementing
"scoped", but it's an implementation of the intent and not the expression
itself. (I purposefully call it "file" in this context because "private"
gives the illusion that it means something else.)

It's called "private" now because it serves as both the "file" level access
and as the implementation of what most people are used to calling
"private". It's a great solution for allowing access to interdependent
types, but using the same implementation to express "scoped" lacks clarity
and imposes file structure that shouldn't be necessary for this reason.

On Mon, Feb 29, 2016 at 7:07 AM Jonathan Tang <jonathan.d.tang at gmail.com>
wrote:

> On Sat, Feb 27, 2016 at 5:18 PM, Ilya Belenkiy via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> Allowing extensions within a module to introduce stored properties would
>> be a great addition to the language, but it's a completely different
>> matter. A language shouldn't dictate file structure. It may make sense to
>> keep related classes in the same file for easier access rather than having
>> tiny files for every scope. "scoped" is much clearer about intent then
>> "private". It says that something must be hidden in the scope regardless of
>> where the code is.
>>
>>
> -1.  I don't agree with this premise; IMHO, a language *should* dictate
> file structure, and aligning code structure with symbol visibility is a lot
> better than aligning it with VM classloading (Java) or with the benevolent
> dictator's preferences (RoR or Angular).  I'd like to see intended symbol
> visibility used as a decision-maker for how to break up code into files:
> this often results in cleaner codebases than if people follow the natural
> tendency to dump everything in one file and then delimit access by scope.
> Lots of small files results in fewer edit conflicts, easier navigation
> through file browsers, and some level of self-documentation through
> filenames.
>
> I spent several years programming in Java, and I don't think that the
> one-public-class-per-file rule was *that* bad.  In the cases where it
> completely fell down - which were usually things like HashMap, where you
> needed a helper class, or interfaces & event handlers, where you had a tiny
> file just needed to define a few methods - Swift's system is much better.
> You'd put the helper classes for HashMap in the same file as the main
> class, where most of the implementation can remain private but any
> protocols can be explicitly declared public.  You can also put protocols in
> the same file and mark them public.
>
> I'm unconvinced that Drew's example wouldn't be better served by putting
> the queue in its own file, where the implementation isn't just private to
> the compiler, but doesn't need to be looked at by anyone browsing through
> the client code.
>
> I do think that it would be handy to have some level of access control
> between file and framework, for when you want to deliver all of your API to
> customers as a unit but may have individual subsystems (written by
> different teams) hiding behind it.  But I'd rather see that solved by
> letting frameworks depend upon other frameworks and yet still be packaged
> together as a unit than by introducing another level of access control.
>
>
>> On Sat, Feb 27, 2016 at 8:04 PM Joe Groff via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> Drew Crawford via swift-evolution
>>> <swift-evolution at swift.org> wrote:
>>> >> I’m unsure what you are imagining here - compiler protection of class
>>> >> details from within a closure completion handler that is also part of
>>> >> your class definition?
>>> >
>>> > What i mean here is simply
>>> >
>>> > class Foo {
>>> >       ///it is undefined behavior to access this variable except from
>>> specialQueue
>>> >        private var _specialQueueOnly = 0
>>> >
>>> >       public var specialQueueOnly: Int {
>>> >               var i: I! = nil
>>> >               dispatch_sync(specialQueue) {
>>> >                       i = _specialQueueOnly
>>> >               }
>>> >               return i
>>> >       }
>>> > }
>>> >
>>> > The lynchpin of this defensive programming technique is that only these
>>> > 12 lines of code have any risk of a threading bug, and we can trivially
>>> > inspect the 12 lines.  So our safety hangs on two tentpegs:
>>> >
>>> > 1.  That "private" in "private var _specialQueue" is compiler
>>> enforcement
>>> > against other files trying to access this ivar
>>> > 2.  That the file itself is 12 (or other minimal number of) lines and
>>> > trivially inspectable.
>>> >
>>> > Should we violate any of these constraints, we lose our safety.
>>>
>>> Allowing extensions within a module to introduce stored properties might
>>> be
>>> another possible way of enabling this, since you could then factor the
>>> private storage and its concurrency-safe accessors into a separate file.
>>> Looking further into the future, our concurrency model will hopefully
>>> have
>>> some mechanism for isolating state, by actors or some other means.
>>>
>>> -Joe
>>>
>>> >
>>> >> It may be that I’m unfamiliar with UITableViewCellContentView (as it
>>> is not documented).
>>> >
>>> >
>>> > Well, that is kind of the point: Objective-C's access control worked,
>>> and
>>> > prevented you from knowing about this class.
>>> >
>>> > But to provide a more accessible illustration, consider the case where
>>> we
>>> > have some motivation to hide Foo from the rest of our
>>> > framework[/module/target/application/executable/library].  This is more
>>> > likely to happen in a UIKit-sized project, where there are hundreds of
>>> > public classes, and probably thousands of "internal" ones, and a
>>> typical
>>> > class has motivation to touch 5 or 6 other classes, of the thousands
>>> that may be available.
>>> >
>>> > In Swift, each internal class is visible to every other class.  But
>>> that
>>> > is not especially workable at UIKit scale; if every class can
>>> potentially
>>> > reach every other class we are in for an adventure when one of the
>>> > hundred developers on your team decides that some UITableView
>>> > implementation detail you've never heard of should be accessing some
>>> > UILocalNotification detail you've also never heard of.  So we need some
>>> > kind of "fencing" within a large framework to make good neighbors.
>>> >
>>> > This is solved very easily: we can group several related classes into
>>> one
>>> > file, and some of the classes are private.  Many ordinary people today
>>> > group related classes into a file as a fencing mechanism even not at
>>> > UIKit-scale.  So a file can access all of its own classes, but not all
>>> > the classes of other files.  That creates the "fence", and now your
>>> > coworker cannot draw a line between some UITableView secret class and
>>> some
>>> > UILocalNotification secret class, and your desk will not be dented from
>>> > the impact of your forehead.
>>> >
>>> > The problem now is that while fixing this situation we have broken one
>>> of
>>> > our safety tentpegs.  We earlier required that Foo.swift be only 12
>>> lines
>>> > for thread safety, but now Foo is contained in a larger file so as to
>>> > create a fence.  So we can solve one of these problems or the other
>>> one,
>>> > but never both at the same time.
>>> >
>>> > "local" effectively resolves this dilemma, because if our
>>> > _specialQueueOnly variable is local, then it is not the /file/ which
>>> must
>>> > be kept to 12 lines, but the /scope/.  So we could group an unlimited
>>> > number of classes in Foo.swift, with no loss of confidence for our
>>> thread safety.
>>> >
>>> > A better approach might be to realize that if global scope, target
>>> scope,
>>> > and file scope do not solve the visibility problem, perhaps yet another
>>> > scope will not totally solve the problem either.  I fully expect Apple
>>> > will need a "vendor" scope for example (so that UIKit and
>>> CoreAnimation,
>>> > two public frameworks, can still have private APIs between them), and I
>>> > bet there are many more kinds of scopes that have not yet occurred to
>>> me.
>>> >
>>> > Behind that realization lies the Rust system, which divorces visibility
>>> > from these arbitrary scopes that we seem to be struggling to fit into.
>>> > But that proposal isn't before us, and this one is.  I prefer going
>>> > somewhere to staying here.
>>> >
>>> > Drew
>>> >
>>> >
>>> >> On Feb 26, 2016, at 10:34 PM, David Waite
>>> >> <david at alkaline-solutions.com> wrote:
>>> >>
>>> >>
>>> >>> On Feb 26, 2016, at 8:44 PM, Drew Crawford via swift-evolution
>>> >>> <swift-evolution at swift.org
>>> >>> <mailto:swift-evolution at swift.org>> wrote:
>>> >>> Threading is one especially pernicious case.  If I have an ivar that
>>> is
>>> >>> only safe for access from one thread, I *need* compiler
>>> enforcement.  I
>>> >>> *need* a guarantee that this ivar is only accessed through public
>>> >>> interface methods that can be audited to be threadsafe.  Simply a
>>> >>> doccomment that says "bad programmer, don't do it" is not enough.
>>> >>
>>> >> I’m unsure what you are imagining here - compiler protection of class
>>> >> details from within a closure completion handler that is also part of
>>> >> your class definition? I believe you would only get this with this
>>> local
>>> >> scope proposal if you structured your code such that callback blocks
>>> >> were functions outside your type definition.
>>> >>
>>> >> If you are talking about access or modification of the inner state of
>>> a
>>> >> class and a manual audit of safety, that audit is of the file and not
>>> of
>>> >> the type or extension. I’m unsure if your concern is of having to
>>> split
>>> >> code into multiple files for safety, or that there is not a way to
>>> split
>>> >> code into multiple files to achieve safety in some particular
>>> scenario.
>>> >>
>>> >>>
>>> >>> This is not even a matter of "artistic choice" of whether or not I
>>> want
>>> >>> to follow "one file per class".  I can achieve thread safety with
>>> >>> "private" ivars and "one file per class", but if my class is
>>> >>> UITableViewCellContentView (which is an implementation detail that
>>> >>> should be hidden even to most of UIKit) I am now forced to expose
>>> that
>>> >>> implementation detail to my entire team.
>>> >>
>>> >>> This places me in the unconscionable situation of choosing between
>>> >>> whether I have thread safety or encapsulation, between whether my
>>> >>> coworker will accidentally create a threading bug or accidentally
>>> use a
>>> >>> class they ought not to use and I am unable to appropriately hide.
>>> >>
>>> >> I’m not quite sure what you mean here - exposing that your class is a
>>> >> subclass of UITableViewCellContentView? Or that the rest of your team
>>> >> needs to code in different files from your class in order to maintain
>>> encapsulation?
>>> >>
>>> >> It may be that I’m unfamiliar with UITableViewCellContentView (as it
>>> is not documented).
>>> >>
>>> >> <snip>
>>> >>
>>> >>>> How much effort did you put into your review? A glance, a quick
>>> >>>> reading, or an in-depth study?
>>> >>> I've followed this from the earliest discussions.  I've rethought my
>>> >>> position somewhat in response to the growing uncertainty about
>>> dropping
>>> >>> the NS prefix, which I think exposes some very real problems with
>>> visibility in Swift.
>>> >>>
>>> >>> As that situation has developed, I no longer believe this proposal
>>> goes
>>> >>> far enough.  But it does go somewhere, and we should not stay where
>>> we are.
>>> >>
>>> >> I don’t understand how an access control proposal pertains to changes
>>> in
>>> >> the Foundation public API. What is the ideal end state in your mind,
>>> and
>>> >> in what way is this a step toward that?
>>> >>
>>> >> -DW
>>> >>
>>> >>
>>> >
>>> >
>>> >
>>>
>>>
>>>
>>>
>>> _______________________________________________
>>> 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
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160301/b9faa4b8/attachment.html>


More information about the swift-evolution mailing list