I'm not sure of my own opinions on this one as I can see good points on
both sides. There also seem to be a variety of different discussions with
similar concerns. So at the risk of confusing things I'm suggesting another
idea, with the hope/intention of promoting some deeper thought and helping
us figure out what is needed/wanted.

I will use the word 'sheltered' here, apologies if that is confusing, it
just means protected/hidden/whatever but without any existing notions of
what those might mean. It might not be the best word either, I just picked
it from a list of synonyms for 'protected'.

Class authors can (now) if they want provide their own way to explicitly
access some private members through a public interface:

public class Thing {
    private var x: Int
    public var shelteredX: Int {
        get { return x }
        set { x = newValue }

But this isn't ideal as it just causes excess boilerplate code and doesn't
hide it from anyone or suggest they shouldn't normally use it (other than
the explicit naming). However, perhaps the compiler could help with this
type of pattern as follows:

public class Thing {
    sheltered var x: Int {
        get { return x } // not sure how this would work - x is also the
backing ivar so you don't have to declare it twice as _x and x.
        set { x = newValue }
   // accessors provided - internal access to x is direct, sheltered access
is via accessors

    sheltered var y: Int
    // No accessors provided, sheltered access to y is direct

For members with 'sheltered' access:
* Don't by default allow access to the sheltered members or accessors -
they are equivalent to private (or maybe fileprivate)
* Provide a way to expose them in a given scope
* I think it can be applied to methods too

For example:
var aThing = Thing()
aThing.x = 7 // NOT ALLOWED: x is sheltered.
@unshelter(aThing) // aThing is unsheltered in the remainder of its scope.
aThing.x = 7 // NOW ALLOWED

One good thing is that this is very explicit where you use it.

Sheltered groups might also be a thing:

sheltered<Subclasses> var x: Int
sheltered<Wotsits> var y: Int
// x is available, y isn't
@unshelter<Subclasses, Wotsits>(aThing)
// x and y are available

"Subclasses" and "Wotsits" are author-defined identifiers, not keywords.
This is a bit like friends and protected, but using an honesty system,
where friends can easily declare themselves as friends by unsheltering all
the Wotsits group, and subclasses (not enforced) would unshelter the
Subclasses group. That is, the author's intentions are clear, but other
classes can abuse it to get access. If the author doesn't want to allow
abuse, they can do this:

sheltered<Subclasses: Thing> var x: Int
sheltered<Wotsits: Wotsit> var y: Int

@unshelter<Subclasses>(self); or
// Works inside a Thing subclass, x becomes available. The scope in which
@unshelter is used must match the type of the shelter group.

@unshelter<Wotsits>(self); or
// Inside a Wotsit or a Wotsit subclass this would work, y becomes

I'm just spitballing here, this isn't fully thought through. Perhaps a
where clause would be better instead of a type annotation for the shelter
groups, so you could list multiple types that are allowed to unshelter that
group. You might be able to abuse it still by putting something in an
extension of an allowed class, so that may need some further thought.

This is of course more complex that other proposed solutions, I'm just
trying to combine all the desires people seem to have and see what people
think - do we want more control and are happy with a bit more complexity?
or should it be simple and less control? Personally I actually don't have
much of an opinion on it because I haven't used Swift enough yet to come a
cropper of such things.
swift-evolution mailing list

Reply via email to