[swift-evolution] [Review] SE-0025 Scoped Access Level
plx
plxswift at icloud.com
Sat Feb 27 09:27:30 CST 2016
> What is your evaluation of the proposal?
Short form: the problem addressed is very real and IMHO very significant; the proposed solution is *useful* but on reflection seems to illustrate this isn’t a problem that can be *entirely* addressed just by adding another scope (or scope(s)). I think I agree with what I think Drew has been saying that the visibility model itself may be not quite ideal (it isn’t broken or anything).
I’m also a little unsure how useful `local` will be in the context of the rest of the language at this time; it seems like to really come into its own we’d also want e.g. to be able to split up the definition of a type across multiple extensions within the same module.
Longer form: within Swift’s existing access-control approach, if you want to keep things private—as opposed to merely internal—it’s easy to wind up with numerous closely-related types, all living in the same file. The only sensible visibility for most of the aspects of most of the types is `private`, but because these are all living in the same file we don’t actually get any effective "cross-type” access-protection within that file.
So e.g. for a custom type implementing `CollectionType`, you can have the type itself, an associated index type, and perhaps also an associated generator type; if instead of “is-a `CollectionType`” you instead opt for “has-multiple `CollectionType` views”, then you can wind up with the base type + 2-3 closely-related types per such view (the collection type, its index, and perhaps also the generator).
The standard-library `String` is an example, where it’s not itself a collection but has 4 “views”, each of which defines an index, and one of which also adds a custom generator.
I don’t have a short example I can share, but I can describe one: at one point I wrote an “accelerated 2D point array”, which was effectively an array of `CGPoint`, but in struct-of-arrays style (e.g. separate arrays of `x` and `y` coordinates). The tricky part is it had a delicate internal state, since the actual storage looked like this (many details elided here) :
@implementation PointArray {
float *_bufferA;
float *_bufferB;
float *_bufferC;
}
@property(nonatomic, assign) PointArrayState state;
@end
…wherein *which* buffer was for `x` or `y` varied over time (and was tracked via that `state` field). The reason for this design is many of the array-level operations were implemented using functions from Accelerate, which in turn often benefit from being done “out-of-place” (and thus e.g. to translate each point by dx,dy, if you started out with `bufferA` having the x-coordinates and `bufferB` having the y-coordinates, you might wind up with `bufferC` holding the updated x-coordinates and `bufferA` holding the updated y-coordinates, along with `state` updated to reflect that updated arrangement).
This is code that would arguably benefit from being migrated to Swift at some point. As it is in Objective-C, it’s implemented as part of a larger family of classes, and it’s easy to tightly control visibility: there’s public headers, there’s private headers imported by subclasses, and there’s methods defined in .m files that are difficult to call accidentally from anywhere else.
In Swift the design would change a bit — a lot of methods that in Objective-C are effectively “sort if necessary, then call this block on each point” would get migrated to “view structs” that implement `CollectionType` — but I get nervous thinking through the implementation because it seems at least a little awkward to structure the internal API in a robust way, while also having so many types all defined in the same file.
I’m not entirely sold that `local` is the ideal solution, but it’s definitely addressing an IMHO significant problem.
What does concern me about `local` is that, at present, it would seemingly force an unnatural code organization at times; the norm is typically to put each adopted protocol into its own extension, but this means that any stored fields must either be `private` — and thus not protected by `local` — or you have to implement a lot of things from within the main definition (not idiomatic).
Even then, consider a simple case like this:
public struct SomeCustomIndex<T> {
local let position: Position
}
public func ==<T>(lhs: SomeViewIndex<T>, rhs: SomeViewIndex<T>) -> Bool {
return lhs.position == rhs.position // oops!
}
…(in this case it’s hard to see what harm you could do with `private let position`, but it illustrates a general limit to the utility of `local` declarations).
> Is the problem being addressed significant enough to warrant a change to Swift?
Yes, VERY MUCH SO.
> Does this proposal fit well with the feel and direction of Swift?
I’m not sure. I feel like there should be some better approach to visibility that solves this problem and is overall more “Swift”, but I don’t know what it is.
> If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
I think the closest analogy here is that "private + local" are trying to get Swift to the same place that, say, “protected + friend” would get it, but along a different route.
The general problem being solved is that Swift’s somewhat-unique approach to `private` is unsatisfactory for some cases—IMHO in particular for the “family of related types” scenario—and `local` adds another tool to solve it.
Although I’m well aware of the arguments against “protected (+ friend)”, I’d point out that those constructs are IMHO the *usual* approach to solving these same issues.
> How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
Read the proposal, participated in original discussion, read the current discussion.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160227/80fd6c81/attachment.html>
More information about the swift-evolution
mailing list