Introduce type-private access level

I agree.

I think Swift's OOP is there mostly for Objective-C interoperability and enabling some edge cases like implementing mutable type-erasing wrappers. In fact, implementing a type-erasing wrapper is literally the only case where I actively want to use class inheritance. The vast majority of Swift classes in existence (not counting those that work with Objective-C) are final root classes. I think Swift's protocol-oriented paradigm is far superior to OOP and as the generics system comes closer and closer to full implementation, the need for OOP will diminish more and more.

I agree that this issue is one that should be solved. It should be possible to access private variables in a type from an extension of that type that resides in another file in the same module. I also would like to have the ability to declare stored variables in extensions that reside anywhere in the same module, and certainly in the same file (but that isn't a point made in this pitch).

However, I don't think that adding a new access level keyword is either likely to happen or the best way to do this. Simply expand the private access level to allow access from extensions in the same module. I think anyone would instantly understand what this means, it doesn't interfere with existing code, it prevents having to change a variable's access level to internal when the intention is that it be private.

Okay, that's a more narrow idea that I kind of agree with, though I worry sometimes that it might produce extension abuse. Not sure if that feeling is based in fact, so whatever.

That said, I do see the use of file private, so helper functions don't pollute the general namespace.

Or maybe we should. I'm genuinely curious: can you share a real-world example of what you mention? That is, extending a type with a function that uses private state in a completely different spot in the project (because it should be there).

The idea of private members is mostly to prevent an object from unsafe modification. Letā€s say you have a class Percent with a private variable and set function that clamps the inout value to 0..100 on write. If you can access that variable directly you can set it to any other number and your class will break. Your proposal makes it possible to override private specification with a caveat that the end user would have to write more code. So the only result achieved is the code will look more complex and the language gets more complex. If you want the user to be able to override private just do not make it private.

Your example is not relevant. The proposal wishes to limit typeprivate access to the same module. There is no loss of safety, as anyone who can add an extension within the module can also modify the original type.

This pitch is aimed at helping people organize their code while maintaining some level of safety from themselves.

5 Likes

What's the difference between who can access an internal member and who can access a typeprivate member? There is none. The only difference is that the coder who attempts to access the typeprivate member might have to write an extra extension.

Submodules or namespaces seem like a better solution to the problem.

Assuming that the developer is actively trying to shoot themself in the foot.

More realistically, the difference is that the developer who attempts to access the typeprivate member says "oh, whoops" upon seeing the compiler error, and then changes their code to use the access point that the type is supposed to use, instead of trying to access its private state. Or, if it turns out that it does make sense to access this member from outside the type, the developer may just change its access level to internal or public, which they can do because they have access to the source.

I don't think that "protect against devious developers who try to circumvent access control that they put into their own source code themselves" is the goal of this proposal.

1 Like

If the developer isn't actively trying to shoot themselves in the foot then I see no reason for an additional access level aside from "modules are too heavy to use for granular groupings." And there are better solutions to that problem.

Our code base includes lots of extensions that would greatly benefit from a typeprivate access level. When creating a class it's obviously the right thing to do to mark private things private, but then we often have to open them up for use in extensions.

The extensions we're using are primarily for splitting up the code in to more manageable, related chunks; sometimes only a few lines long - but we find many benefits of structuring the project this way.

I can certainly understand the shortcomings of typeprivate, but overall it would be greatly received and improve our codebase considerably.

3 Likes

Sorry but there seems to be no real argument or explanation for the distinction here. You're presenting 2 cases:

  • the developer mistakenly tries to access a private member, and then switches to the internal interface;
  • the developer realizes that a private member should be internal.

What's the third case?

Over the years I found that for us the opposite was true: our codebase had a lot of completely unnecessary splitting around, that made the project less manageable, not more. But we also realized that some business-specific types did too much, and the solution was not to split a file in multiple files, but to try and follow more closely the single responsibility principle, where a type (business-specific, this doesn't apply to generic reusable constructions) is small on purpose, and there's no reason to not have its full definition into a file.

That's why I'm genuinely curious: can you provide some real-world examples of extensions that should be on separate files but would need a typeprivate access level? I'm assuming, of course, that you don't mean "protected access for subclasses", which is something that Swift doesn't support, and for good reasons.

1 Like

Iā€™m going to give this idea a +100. The sheer amount of times Iā€™ve wanted something like this, where I can say ā€œlimit to any extensions or subtypes of this typeā€.

I see some people donā€™t like this idea and wouldnā€™t use it, but I havenā€™t seen any arguments that state why it shouldnā€™t be implemented for those who would find it incredibly useful. If Swift wants to be such a heavily compiler based language then this would be a great tool to allow compiler checking of the semantics of code.

That said, if extremely lightweight submodules could happen (almost to the point that you could make an Xcode group a sub module), that would also solve a lot of this. Either way, I strongly believe we need at least one of these soon as Iā€™m constantly irritated by the Swift compiler limiting my ability to properly express the code I want, and now we have the generic fixes in Swift 5.7, this is now my number 1 problem area

1 Like

Whether lexical or semantical, whatever the implications of that are, this is a feature Iā€™ve wanted for more than five years. If you want to control access to your typeā€™s private implementation, even in your own library, it seems like a needless restriction having to code it all in one file.

1 Like

I think it's a great limitation. IMHO large types are a signal that they should be broken up.

2 Likes

Protocol conformance is generally a good candidate for separate files, IMO. Having to implement in the main file, because you use private API, takes that possibility away.

You can still break up your types, even with a typeprivate access level available.

5 Likes

One way to break them up is by using extensions.

This has been raised probably a dozen times here.

One alternative to "typeprivate" is to introduce a new lightweight concept, let's straw-man call it "type component". It is like an extension but:

  • type component can access private fields.

  • you need to modify the main type in order to add a component (in the same or a different file):

	// file1
    struct Foo {
    	components A, B, C
    	private var x = 0
    	fileprivate var y = 0
    }
    
    // file2
    component Foo.A {
    	func test() {
    		print(x) // ok
    		print(y) // error
    	}
    }

If there's a concern how (secure?, etc) private variables of a type are used - the same amount of scrutiny should be applied to the type components as to the type itself. This concept is somewhat similar to C++ friends (and their explicit declaration).

This isnā€™t necessarily just for large types, it can help for smaller types too. This was actually a fairly easy pattern to achieve in Objective-C as you would just create a separate ā€œFoo-Private.hā€ header file that anything that needed ā€œtype privateā€ access could import. This would re-introduce that same flexibility in API design

I have also wanted typeprivate many many many times. There are lots of situations around it, but they all center around balancing the tension between "this thing has private state that should only be accessible by the thing itself" and "this primary implementation of this thing is growing very large and I want to split it into multiple extensions and/or multiple files". But doing that means I can't mark the property as private anymore, because then it's not visible to extensions. And fileprivate isn't that much better, because it still precludes me from organizing extensions into different files.

This is a bit beyond the scope of the specific request here, but I still believe that the holy grail of access level proposals is this one from @Erica_Sadun. It provides the flexibility to define typeprivate and forSubtypesOnly and "friend" types and a whole host of other combinations.

6 Likes

There is a use case for this when implementing advanced custom UI components with AppKit/UIKit, where a custom NSView subclass might want to handle a ton of NSResponder methods. The class can end up getting very heavy, so I split the files into CustomView.swift, CustomView+NSResponder.swift using extensions. I sometimes may want to exclude one of these chunks when targeting another platform (e.g. iOS).

Hereā€™s an Obj-C example from WebKit that attempts to fulfil all itā€™s view/responder obligations in one 4000 line file: WebKit/WKWebView.mm at main Ā· WebKit/WebKit Ā· GitHub

What I would like is either something similar to typeprivate, to share member access privately across extensions, or some way of indicating to the compiler that a file has been split into multiple pieces, and can be reassembled into one contiguous ā€™fileā€™ at build time.

I know there are strong ideological opinions on this, an argument being that people will supposedly be encouraged to write worse codeā€¦ but people are routinely writing even worse code at the moment to work around this.

I've only really run into this issue in Cocoa-land, with classes specifically. Maybe there's an argument to be made to constrain this feature to classes?

1 Like