Apply the class access control value to default initializer

My proposal is to set the access control value of a class also to his default initializer.

It is true that by default all the methods are marked as internal, and is a great choice , but I think that the default initializer since is automatically added can be an exception for this rule, because most of the times a "public" class needs to be instantiated also from an entity outside the current module.

With this solution there is always the possibility to add a init() { } for mark the initializer to internal if there is the need to not make a public class not instantiable form outside the current module .

The advantage provided by this proposal is that when developing a framework you don't have to write in all public classes the public init() { } that is just annoying code and easy to forget.
This can simplify the deployment of frameworks and reduce the number of iteration before having a module with the right access control for each initializer.

Example

Current Situation

Internal Class in a Module

class MyInternalClass {
}

Public Class in a Module instantiable from outside the Module itself

public class MyPublicClass {
    public init() {
    }
}

Proposed Solution

Internal Class in a Module

class MyInternalClass {
}

Public Class in a Module instantiable from outside the Module itself

The initializer inherits the public access control from the class declaration

public class MyPublicClass {
}

Actually this is by design and exactly the other way around, we as API developer should not accidentally expose the synthesized init's to the public, that's why it's our responsibility to make public API explicit regardless ergonomics.

This topic was previously discussed a couple of times. If you wish to pursue a change it might help if you dig up the old threads and summarize the satatus quo.

And by this point of time this would result in a huge breaking change since it would expose internal API to the public.

7 Likes

I realize this is retreading old territory, but I think the real solution here is some way of explicitly exposing the synthesized init without having to re-implement it by hand. Maybe with an attribute. Hopefully, we'll be able to take a look at this after ABI stability rolls out.

2 Likes

I agree with you, if we're going to provide a solution for this, then it should be a simple and easy memorizable attribute. Since it would remain an explicit thing it won't break anything, but it must be shorter than open/public init() {} of course.

It could be as simple as @open/public(init) and maybe @private(init), but that's more niche.

I think that collides to much with the access modifier and could be too confusing. But something along the lines for sure.

1 Like

Maybe it would be better if we inverted it: @initable(open/public)

The problem with having an attribute attached to the type that causes the default memberwise initializer to be made public is that the type author can still modify the stored properties in their class and have that silently change the signature of the type's initializer in a breaking way. That's not safe.

Fortunately, this space was explored pretty deeply and more generally in SE-0018, very early on in Swift's evolution process. Its state was deferred; in particular, see the quite detailed rationale for the core team's thoughts at the time and (at the bottom) a solution closer to one that they more strongly liked. Their solution addresses the problem above nicely: by tagging not only an abbreviated initializer but also the properties that participate in memberwise initialization, you eliminate the harmful side effect of unintentionally changing the signature of a public API.

Any new work in this space should involve building on that original proposal and addressing the points in the rationale, and I presume @anandabits as the original author would want to be involved in that.

2 Likes

Thanks @allevato, I didn't have a chance to reply here yet. I would really love to revisit memberwise initialization. It would fit really nicely alongside the Equatable, Hashable and Codable synthesis we have now.

The primary holdup is that I don't have bandwidth to work on implementation which is now required for all proposals. If anyone able to work on a prototype is willing to collaborate please let me know!

1 Like