On Sat, Feb 27, 2016 at 10:27 AM plx via swift-evolution < swift-evolution@swift.org> wrote:
- 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.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution