final + lazy + fileprivate modifiers


(Joanna Carter) #1

It's not that it's not meant to be called, but to be called from certain context. Coming back to a codebase after a while, you don't need to remember that method is not meant to be called out of certain context and if you name your methods well, you don't really need to go and take a look at what the method does, so you might miss this fact since you didn't go and read the docs. Xcode wouldn't even offer you this method in autocompletion if it were marked protected.

I'm not saying that this is something the Swift community can't live without, but it's of a huge convenience for newcomers to a project for whom the IDE wouldn't offer methods that should not get called from outside of the enclosing type.

I believe we are running to a point where the community is divided into two major camps - one part that would like to get the access control almost eliminated as they don't the see it that much useful; and second part that on the other hand would very much like to see extended access control - not by files/modules, but by the enclosing type. People are unlikely to switch these camps as their view on the subject is mostly shaped by their previous experience with working on team projects.

IMHO, those who have previously worked with other experienced and responsible developers are probably advocates of less access control; those who have previously worked will less experienced developers are most likely pushing forward a more extended access control model.

I've previously worked as a lead developer in a few start-up businesses where people came and went and it was unnecessary burden to point to them why they shouldn't do something that way, that they overlooked that the member should not be invoked out of the enclosing context because they didn't full read documentation for that method; when you do this with a 10th person for the 100th time, you start wondering if this is one of those things that the language should help with a little by providing means of preventing people of misusing/abusing certain parts of the API.

Charlie, you really have hit the nail fairly and squarely on the head! :slight_smile:

It is all very well and good for experienced developers to say that others should read the documentation but, do you know what? After more than 25 years as a developer (including for languages that didn't have an IDE with autocompletion), I still rely heavily on autocompletion to help me through the morass that most frameworks ressemble.

The time saved be being able to see, at a glance, in the code that you are writing, what you can and cannot use, must run into years. Yes, I do look some stuff up in the UIKit and Cocoa source files and the documentation but that all takes time.

I have also worked as a lead developer, as well as a creator of frameworks and, as you rightly said, it is the inexperienced (or just darned lazy) developers who will not read anything other than that which autocompletion offers.

I once gave a task to such a developer, who then took three days to work out their own implementation of a small class hierarchy. When I pressed them to allow me to integrate their code, I found that it was incompatible with the rest of the system and was allocating memory in one file and deallocating it in another!

It took me half an hour to rewrite the whole thing from scratch.

The companies I have worked with don't want to have to allocate expensive resources like us to routine tasks. Autocompletion is an absolute godsend for both the inexperienced and experienced. Without good access control, enforced by autocompletion, reliable code takes longer to write and costs more.

···

--
Joanna Carter
Carter Consulting


(David Hart) #2

I don't agree with this point. I'm for a file-based private, but I definitely do NOT want access control eliminated, quite the contrary. I think the arguments are much subtle than what is presented here. I want strong access control, and to have that, the access control modifiers should be powerful and few do they are actually used. Right now we have private and fileprivate and many people don't use them consistently because they are so similar. We need only one private access control and I'm voting for fileprivate because it is the one which is the simplest while still working well with Swift's extension mechanisms.

···

On 20 Feb 2017, at 12:38, Joanna Carter via swift-evolution <swift-evolution@swift.org> wrote:

It's not that it's not meant to be called, but to be called from certain context. Coming back to a codebase after a while, you don't need to remember that method is not meant to be called out of certain context and if you name your methods well, you don't really need to go and take a look at what the method does, so you might miss this fact since you didn't go and read the docs. Xcode wouldn't even offer you this method in autocompletion if it were marked protected.

I'm not saying that this is something the Swift community can't live without, but it's of a huge convenience for newcomers to a project for whom the IDE wouldn't offer methods that should not get called from outside of the enclosing type.

I believe we are running to a point where the community is divided into two major camps - one part that would like to get the access control almost eliminated as they don't the see it that much useful; and second part that on the other hand would very much like to see extended access control - not by files/modules, but by the enclosing type. People are unlikely to switch these camps as their view on the subject is mostly shaped by their previous experience with working on team projects.

IMHO, those who have previously worked with other experienced and responsible developers are probably advocates of less access control; those who have previously worked will less experienced developers are most likely pushing forward a more extended access control model.

I've previously worked as a lead developer in a few start-up businesses where people came and went and it was unnecessary burden to point to them why they shouldn't do something that way, that they overlooked that the member should not be invoked out of the enclosing context because they didn't full read documentation for that method; when you do this with a 10th person for the 100th time, you start wondering if this is one of those things that the language should help with a little by providing means of preventing people of misusing/abusing certain parts of the API.

Charlie, you really have hit the nail fairly and squarely on the head! :slight_smile:

It is all very well and good for experienced developers to say that others should read the documentation but, do you know what? After more than 25 years as a developer (including for languages that didn't have an IDE with autocompletion), I still rely heavily on autocompletion to help me through the morass that most frameworks ressemble.

The time saved be being able to see, at a glance, in the code that you are writing, what you can and cannot use, must run into years. Yes, I do look some stuff up in the UIKit and Cocoa source files and the documentation but that all takes time.

I have also worked as a lead developer, as well as a creator of frameworks and, as you rightly said, it is the inexperienced (or just darned lazy) developers who will not read anything other than that which autocompletion offers.

I once gave a task to such a developer, who then took three days to work out their own implementation of a small class hierarchy. When I pressed them to allow me to integrate their code, I found that it was incompatible with the rest of the system and was allocating memory in one file and deallocating it in another!

It took me half an hour to rewrite the whole thing from scratch.

The companies I have worked with don't want to have to allocate expensive resources like us to routine tasks. Autocompletion is an absolute godsend for both the inexperienced and experienced. Without good access control, enforced by autocompletion, reliable code takes longer to write and costs more.

--
Joanna Carter
Carter Consulting

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joanna Carter) #3

But the problem is, fileprivate may work well with Swift's extension mechanism but only if you place those extensions in the same file.

What I am fighting for is an access specifier that allows us to extend types in another file, to avoid the gargantuan file syndrome that can easily ensue if you need many extensions to the type.

Don't forget, if it's visible to another file, it can be abused by the inexperienced working in the same module

···

Le 20 févr. 2017 à 13:29, David Hart <david@hartbit.com> a écrit :

I don't agree with this point. I'm for a file-based private, but I definitely do NOT want access control eliminated, quite the contrary. I think the arguments are much subtle than what is presented here. I want strong access control, and to have that, the access control modifiers should be powerful and few do they are actually used. Right now we have private and fileprivate and many people don't use them consistently because they are so similar. We need only one private access control and I'm voting for fileprivate because it is the one which is the simplest while still working well with Swift's extension mechanisms.

--
Joanna Carter
Carter Consulting


(Dimitri Racordon) #4

I think that fileprivate is fine, and that the drawback of having very large files is acceptable, compared to additional complexity induced by more subtle access control syntax/semantics. fileprivate has the privilege to be easy to understand by *anyone*; extensible, scoped, protected and the likes are not.

Also, I don’t think the expressivity of access control should cover every single case:
* What if I want another type to access my internal properties?
* What if I want another type to access a subset of my internal properties?
* What if I want another type to access a subset of my internal properties in read-only?
* What if I want another *function* to access my internal properties?
* …

I’m sure everybody can find good examples illustrating the above questions, and I’m sure some people already encountered similar situations. I may even agree that in these instances a super-fine-tuned access control might have saved few hours of work, but nobody seem to consider the additional cost of having those in the first place. If it takes days for (in)experienced users to understand the meaning of an access control modifier, plus hours and hours of debating with your team whether some type/function/property should be private, file private, type private, extensible private, module private, etc. the potential time saved by having access control is lost by having to define it properly.


(Joanna Carter) #5

I think that fileprivate is fine, and that the drawback of having very large files is acceptable,

Having had to debug code for my clients in past years, I can say that, from real world experience, large code files are a $&§*% nightmare.

They are an absolute dog to maintain if you are working in a team environment because, if one person wants to work on one extension and another wants to work on a second, the source control implications are horrendous with merges, conflicts, etc, all over the shop.

Of course, if you are a lone developer, then it's up to you how long you take to find bugs and fix them and, as for source control, there is no problem with all those merges and conflicts. Just spare a thought for the larger teams.

compared to additional complexity induced by more subtle access control syntax/semantics. fileprivate has the privilege to be easy to understand by *anyone*; extensible, scoped, protected and the likes are not.

Most developers who any experience in any OO language other than Objective-C and Swift are usually extremely well versed in the "standard" class visibilities ; it's one of those things you get taught in the first couple of lectures on any good programming course.

Also, I don’t think the expressivity of access control should cover every single case:
* What if I want another type to access my internal properties?

Use 'extensible' on the properties you want to expose

* What if I want another type to access a subset of my internal properties?

Use 'extensible' on the properties you want to expose

* What if I want another type to access a subset of my internal properties in read-only?

Use 'extensible' on the properties you want to expose

* What if I want another *function* to access my internal properties?

Use 'extensible' on the properties you want to expose

Ah, hang on, that makes… one visibility to learn :wink:

I may even agree that in these instances a super-fine-tuned access control might have saved few hours of work, but nobody seem to consider the additional cost of having those in the first place. If it takes days for (in)experienced users to understand the meaning of an access control modifier,

private - only accessible in the declaring entity
extensible - accessible as for private but also in extending types
internal - accessible anywhere within a module
public - accessible anywhere

You really think it takes days to learn four visibilities? I am truly shocked!

plus hours and hours of debating with your team whether some type/function/property should be private, file private, type private, extensible private, module private, etc. the potential time saved by having access control is lost by having to define it properly.

Of course, if you only use the above four visibilities, the discussions are much shorter :wink:

···

Le 20 févr. 2017 à 15:30, Dimitri Racordon <Dimitri.Racordon@unige.ch> a écrit :

--
Joanna Carter
Carter Consulting


(Dimitri Racordon) #6

Having had to debug code for my clients in past years, I can say that, from real world experience, large code files are a $&§*% nightmare.

This is what I meant when I said that everybody has a story about that time it was so hard to do this because of that. This is not to say that I don’t want to spare a thought for people in large teams. I do recognise the issue, I just don’t think complicated access control is the silver bullet. Hence I’d rather favour simplicity of the language over expressivity of its access control.

If ill-advised users want to do silly things with your awesome type, they will. I’ll bet we could find one person who thinks using reflection is such a clever way to bypass these so-called private properties.

Most developers who any experience in any OO language other than Objective-C and Swift are usually extremely well versed in the "standard" class visibilities ; it's one of those things you get taught in the first couple of lectures on any good programming course.

Yet if you google "private vs protected", the first 5 links are stackoverflow questions on the subject, and the rest of the first page are blog posts like “Pragmatism over Theory: Protected vs Private”. Despite decades of OO, those notions are still confusing for many people, and are intimidating for newcomers.

Besides, I think those results also illustrate my point about the time-consuming debates on what access level something should have.

Ah, hang on, that makes… one visibility to learn :wink:

Nope, it doesn’t. Extensible wouldn’t restrict the visibility as tightly as I described. That said, that list was a shameless exaggeration more than a critique of your proposition.


(Joanna Carter) #7

This is what I meant when I said that everybody has a story about that time it was so hard to do this because of that. This is not to say that I don’t want to spare a thought for people in large teams. I do recognise the issue, I just don’t think complicated access control is the silver bullet. Hence I’d rather favour simplicity of the language over expressivity of its access control.

Which is why I am suggesting simple access control, rather than the complications we are getting now.

If ill-advised users want to do silly things with your awesome type, they will. I’ll bet we could find one person who thinks using reflection is such a clever way to bypass these so-called private properties.

Well, at least if your classes don't inherit from NSObject, reflection won't do them much good :wink:

Yet if you google "private vs protected", the first 5 links are stackoverflow questions on the subject, and the rest of the first page are blog posts like “Pragmatism over Theory: Protected vs Private”. Despite decades of OO, those notions are still confusing for many people, and are intimidating for newcomers.

I suppose the problem is that so many "newcomers" never went to college and learnt the basics. They saw the promise of fame and fortune(?) via the AppStore, went out and got themselves a Mac and started coding without the faintest idea of what a class or a struct are, never mind what scope to use.

Ah, hang on, that makes… one visibility to learn :wink:

Nope, it doesn’t. Extensible wouldn’t restrict the visibility as tightly as I described.

Does it really need that degree of control?

That said, that list was a shameless exaggeration more than a critique of your proposition.

:wink:

···

Le 20 févr. 2017 à 16:59, Dimitri Racordon <Dimitri.Racordon@unige.ch> a écrit :

--
Joanna Carter
Carter Consulting


(Vladimir) #8

I think that fileprivate is fine, and that the drawback of having very large files is acceptable,

Having had to debug code for my clients in past years, I can say that, from real world experience, large code files are a $&§*% nightmare.

They are an absolute dog to maintain if you are working in a team environment because, if one person wants to work on one extension and another wants to work on a second, the source control implications are horrendous with merges, conflicts, etc, all over the shop.

Of course, if you are a lone developer, then it's up to you how long you take to find bugs and fix them and, as for source control, there is no problem with all those merges and conflicts. Just spare a thought for the larger teams.

compared to additional complexity induced by more subtle access control syntax/semantics. fileprivate has the privilege to be easy to understand by *anyone*; extensible, scoped, protected and the likes are not.

Most developers who any experience in any OO language other than Objective-C and Swift are usually extremely well versed in the "standard" class visibilities ; it's one of those things you get taught in the first couple of lectures on any good programming course.

Also, I don’t think the expressivity of access control should cover every single case:
* What if I want another type to access my internal properties?

Use 'extensible' on the properties you want to expose

* What if I want another type to access a subset of my internal properties?

Use 'extensible' on the properties you want to expose

* What if I want another type to access a subset of my internal properties in read-only?

Use 'extensible' on the properties you want to expose

* What if I want another *function* to access my internal properties?

Use 'extensible' on the properties you want to expose

Ah, hang on, that makes… one visibility to learn :wink:

I may even agree that in these instances a super-fine-tuned access control might have saved few hours of work, but nobody seem to consider the additional cost of having those in the first place. If it takes days for (in)experienced users to understand the meaning of an access control modifier,

private - only accessible in the declaring entity
extensible - accessible as for private but also in extending types
internal - accessible anywhere within a module
public - accessible anywhere

Well, in general I support your opinion, but there is one question not answered by your 'extensible' modifier.
It is common to have access to type's internals in the same file from inside just other types("friend" types), not extending types.
So, in this case we want something like fileprivate+extensible if we want also share details for extending types in other files.
And so, IMO, 'extensible' should means 'accessible as for private but also in extending types AND INSIDE CURRENT FILE", or we need one more access modifier.

Also, there is a common(IMO) opinion in the list that we should rename fileprivate to private as in Swift2.

So, I believe this will be optimal minimum of access modifiers set:

public: outside of module
internal: anywhere inside module
private: anywhere in current file
scoped(hidden,*or other keyword*): as current 'private', only inside declared type
extensible : in current file and in subtypes/extensions in other files

···

On 20.02.2017 18:25, Joanna Carter via swift-evolution wrote:

Le 20 févr. 2017 à 15:30, Dimitri Racordon <Dimitri.Racordon@unige.ch> a écrit :

You really think it takes days to learn four visibilities? I am truly shocked!

plus hours and hours of debating with your team whether some type/function/property should be private, file private, type private, extensible private, module private, etc. the potential time saved by having access control is lost by having to define it properly.

Of course, if you only use the above four visibilities, the discussions are much shorter :wink:

--
Joanna Carter
Carter Consulting

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joanna Carter) #9

Well, in general I support your opinion, but there is one question not answered by your 'extensible' modifier.
It is common to have access to type's internals in the same file from inside just other types("friend" types), not extending types.
So, in this case we want something like fileprivate+extensible if we want also share details for extending types in other files.
And so, IMO, 'extensible' should means 'accessible as for private but also in extending types AND INSIDE CURRENT FILE", or we need one more access modifier.

I think that was what I was intending. If 'extensible' was type-based, then, surely, whether the 'friend' was in the same file/module or whatever, the accessibility would be the same?

Also, there is a common(IMO) opinion in the list that we should rename fileprivate to private as in Swift2.

I'm not sure why ; with 'extensible' available to code within the same file, why would you still need fileprivate anywy?

So, I believe this will be optimal minimum of access modifiers set:

public: outside of module
internal: anywhere inside module
private: anywhere in current file
scoped(hidden,*or other keyword*): as current 'private', only inside declared type
extensible : in current file and in subtypes/extensions in other files

Otherwise known in my language as :

public - anywhere
internal - anywhere inside the module
private - only inside current type
extensible - in current file and in subtypes/extensions in other files

The only other option that might be useful is something like 'internal extensible' to limit visibility for extensible members to the current module.

Sorry, I would still like to see a waterproof argument for what is presently called fileprivate ; with the above scopes, I simply can't see the need. Unless you can convince me, that is :wink:

···

Le 20 févr. 2017 à 17:25, Vladimir.S <svabox@gmail.com> a écrit :

--
Joanna Carter
Carter Consulting


(Vladimir) #10

Well, in general I support your opinion, but there is one question not
answered by your 'extensible' modifier. It is common to have access to
type's internals in the same file from inside just other
types("friend" types), not extending types. So, in this case we want
something like fileprivate+extensible if we want also share details
for extending types in other files. And so, IMO, 'extensible' should
means 'accessible as for private but also in extending types AND
INSIDE CURRENT FILE", or we need one more access modifier.

I think that was what I was intending. If 'extensible' was type-based,
then, surely, whether the 'friend' was in the same file/module or
whatever, the accessibility would be the same?

Also, there is a common(IMO) opinion in the list that we should rename
fileprivate to private as in Swift2.

I'm not sure why ; with 'extensible' available to code within the same
file, why would you still need fileprivate anywy?

To provide access for internals of my type for code in current file only,
and don't for any extension/subtype in other files. I.e. 'friend' classes in the same file, but don't want to provide any details out of the file.

So, I believe this will be optimal minimum of access modifiers set:

public: outside of module internal: anywhere inside module private:
anywhere in current file scoped(hidden,*or other keyword*): as current
'private', only inside declared type extensible : in current file and
in subtypes/extensions in other files

Otherwise known in my language as :

public - anywhere internal - anywhere inside the module private - only
inside current type extensible - in current file and in
subtypes/extensions in other files

The only other option that might be useful is something like 'internal
extensible' to limit visibility for extensible members to the current
module.

I assume the 'extensible' modifier should be limited by current module only.
I.e. in my understanding 'extensible' : "in current file and in subtypes/extensions in other files inside the module". I believe there is a consensus that Swift should not export "private" API out of the module.

···

On 20.02.2017 20:04, Joanna Carter wrote:

Le 20 févr. 2017 à 17:25, Vladimir.S <svabox@gmail.com> a écrit :

Sorry, I would still like to see a waterproof argument for what is
presently called fileprivate ; with the above scopes, I simply can't see
the need. Unless you can convince me, that is :wink: -- Joanna Carter
Carter Consulting


(David Waite) #11

Otherwise known in my language as :

public - anywhere
internal - anywhere inside the module
private - only inside current type
extensible - in current file and in subtypes/extensions in other files

The only other option that might be useful is something like 'internal extensible' to limit visibility for extensible members to the current module.

If a type is extensible by other modules at all, I prefer that to be spelled “public”.

Sorry, I would still like to see a waterproof argument for what is presently called fileprivate ; with the above scopes, I simply can't see the need. Unless you can convince me, that is :wink:

In C++ terms, it would be when I want some other class to have friend access to a function/data, but for it not to be arbitrarily accessible by subtypes

I believe the critical piece of designing access levels is for the levels to document the intent of the developer specifying the access level. Does “extensible” indicate that the designer of the library wanted a certain kind of access - or that the compiler maybe gave an error at some point when it was “private”?

-DW

···

On Feb 20, 2017, at 10:04 AM, Joanna Carter via swift-evolution <swift-evolution@swift.org> wrote:


(Joanna Carter) #12

Joanna Carter
Carter Consulting

Envoyé de mon iPad

I'm not sure why ; with 'extensible' available to code within the same
file, why would you still need fileprivate anywy?

To provide access for internals of my type for code in current file only,
and don't for any extension/subtype in other files. I.e. 'friend' classes in the same file, but don't want to provide any details out of the file.

Ah, I can see what you're getting at there. How about the idea of using nested types?

I assume the 'extensible' modifier should be limited by current module only.
I.e. in my understanding 'extensible' : "in current file and in subtypes/extensions in other files inside the module". I believe there is a consensus that Swift should not export "private" API out of the module.

My thinking about extensible is that it is not for truly private stuff ; more for extending the same idea of protected access for classes to the more Swiftier concept of extensions.

···

Le 20 févr. 2017 à 18:40, Vladimir.S <svabox@gmail.com> a écrit :

On 20.02.2017 20:04, Joanna Carter wrote:

--
Joanna Carter
Carter Consulting


(Joanna Carter) #13

The only other option that might be useful is something like 'internal extensible' to limit visibility for extensible members to the current module.

If a type is extensible by other modules at all, I prefer that to be spelled “public”.

Indeed, I could agree with that but…

Before 'open' was added to indicate extensibility of a publicly visible class, we used to have 'public' to indicate that the class was visible anywhere and 'final' to restrict inheritance/overriding.

With classes, 'final' was good enough to indicate that a type and/or its members should not be extensible.

But in the Swift world, we now have the ability to extend almost any type, except Any and AnyObject, which appear to be protected by some deep and dark mechanism within the compiler. So, just as those two protocols cannot be extended, should we not be looking at some generally available mechanism to prevent extensibility of any type?

And, I am not talking visibility here, just extensibility ; somehow those two concerns are often conflated and, I believe, this is the cause of much of the "lively" discussion on visibility specifiers.

In C++ terms, it would be when I want some other class to have friend access to a function/data, but for it not to be arbitrarily accessible by subtypes

Indeed. I do wonder if some folks approach visibility control as an exercise in "what can I see" whereas, demonstrated by C++ friends, it becomes obvious that it is more about "what do I want to allow to be seen"

Is there not a value in forking this discussion into which keywords are truly about visibility control and which are about extensibility control?

I believe the critical piece of designing access levels is for the levels to document the intent of the developer specifying the access level. Does “extensible” indicate that the designer of the library wanted a certain kind of access - or that the compiler maybe gave an error at some point when it was “private”?

This is somewhere where I have to bring up my learning and experience with other languages. Albeit only with classes, private always meant what it said on the tin - from outside of a class, it doesn't exist, don't touch!

Which is why I am very wary of reverting file scope to private.

'fileprivate' is a keyword that allows, not extensibility but visibility.

Extensibility control for non-class types is more akin to using or not using 'final' to control inheritance/overriding with classes.

Maybe, instead of looking at extending visibility by adding yet another visibility specifier (extensible), we should be looking at adding a specifier that limits extensibility for any type?

This would be additive and would have the advantage of not breaking so much code.

OK, how does this sound?

Extend the 'final' concept, currently used only in classes, to protect any, non-protocol, type from being extended.

That way, folks who are not so "purist" about access control can continue as before, leaving those of us who want that control to be able to exercise it.

So, for example :

final struct DontExtendMe
{
  …
}

extension DontExtendMe // error : final type cannot be extended
{
  …
}

I am not suggesting that 'final' should be applicable to members of non-class types. Members of class types should continue to be markable as 'final' for inheritance control reasons.

···

Le 21 févr. 2017 à 05:21, David Waite <david@alkaline-solutions.com> a écrit :

--
Joanna Carter
Carter Consulting


(Chéyo Jiménez) #14

Sorry to beat this like a drum :

How is swift 3 extensibility harmful exactly?

Do you have specific examples when extensibility was harmful in a project?

This probably deserves its own thread if the examples are substantial.

···

On Feb 21, 2017, at 1:27 AM, Joanna Carter via swift-evolution <swift-evolution@swift.org> wrote:

Le 21 févr. 2017 à 05:21, David Waite <david@alkaline-solutions.com> a écrit :

The only other option that might be useful is something like 'internal extensible' to limit visibility for extensible members to the current module.

If a type is extensible by other modules at all, I prefer that to be spelled “public”.

Indeed, I could agree with that but…

Before 'open' was added to indicate extensibility of a publicly visible class, we used to have 'public' to indicate that the class was visible anywhere and 'final' to restrict inheritance/overriding.

With classes, 'final' was good enough to indicate that a type and/or its members should not be extensible.

But in the Swift world, we now have the ability to extend almost any type, except Any and AnyObject, which appear to be protected by some deep and dark mechanism within the compiler. So, just as those two protocols cannot be extended, should we not be looking at some generally available mechanism to prevent extensibility of any type?

And, I am not talking visibility here, just extensibility ; somehow those two concerns are often conflated and, I believe, this is the cause of much of the "lively" discussion on visibility specifiers.

In C++ terms, it would be when I want some other class to have friend access to a function/data, but for it not to be arbitrarily accessible by subtypes

Indeed. I do wonder if some folks approach visibility control as an exercise in "what can I see" whereas, demonstrated by C++ friends, it becomes obvious that it is more about "what do I want to allow to be seen"

Is there not a value in forking this discussion into which keywords are truly about visibility control and which are about extensibility control?

I believe the critical piece of designing access levels is for the levels to document the intent of the developer specifying the access level. Does “extensible” indicate that the designer of the library wanted a certain kind of access - or that the compiler maybe gave an error at some point when it was “private”?

This is somewhere where I have to bring up my learning and experience with other languages. Albeit only with classes, private always meant what it said on the tin - from outside of a class, it doesn't exist, don't touch!

Which is why I am very wary of reverting file scope to private.

'fileprivate' is a keyword that allows, not extensibility but visibility.

Extensibility control for non-class types is more akin to using or not using 'final' to control inheritance/overriding with classes.

Maybe, instead of looking at extending visibility by adding yet another visibility specifier (extensible), we should be looking at adding a specifier that limits extensibility for any type?

This would be additive and would have the advantage of not breaking so much code.

OK, how does this sound?

Extend the 'final' concept, currently used only in classes, to protect any, non-protocol, type from being extended.

That way, folks who are not so "purist" about access control can continue as before, leaving those of us who want that control to be able to exercise it.

So, for example :

final struct DontExtendMe
{

}

extension DontExtendMe // error : final type cannot be extended
{

}

I am not suggesting that 'final' should be applicable to members of non-class types. Members of class types should continue to be markable as 'final' for inheritance control reasons.

--
Joanna Carter
Carter Consulting

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #15

But in the Swift world, we now have the ability to extend almost any type, except Any and AnyObject, which appear to be protected by some deep and dark mechanism within the compiler. So, just as those two protocols cannot be extended, should we not be looking at some generally available mechanism to prevent extensibility of any type?

And, I am not talking visibility here, just extensibility ; somehow those two concerns are often conflated and, I believe, this is the cause of much of the "lively" discussion on visibility specifiers.

This is imho more of an issue with ABI and resiliency. If I can create an extension which adds a method to a type, and that type gains a method with that same signature in a later release, what is the behavior?

In C++ terms, it would be when I want some other class to have friend access to a function/data, but for it not to be arbitrarily accessible by subtypes

Indeed. I do wonder if some folks approach visibility control as an exercise in "what can I see" whereas, demonstrated by C++ friends, it becomes obvious that it is more about "what do I want to allow to be seen"

Is there not a value in forking this discussion into which keywords are truly about visibility control and which are about extensibility control?

Possibly; they are two axes. However, I’m hoping that new access modifiers (including possible submodule functionality) + extensibility modifiers are considered holistically.

For instance, if there is a feature that allows something comparable to ‘friend’ level access, a restrictive private makes a lot more sense than one with exceptions allowing access to subtypes, within the same file, to extensions, etc. A restrictive, scoped private would be what you use to protect the invariants of your type, with less protected methods given to allow extensions and internal modification safely.

But without a submodule or similar access level, we need a “fileprivate” level access (renamed to ‘private’ or not) to make sure code needing a higher level of access can get it, by being embedded in the same file.

OK, how does this sound?

Extend the 'final' concept, currently used only in classes, to protect any, non-protocol, type from being extended.

The extension mechanism, both being able to add new methods and to conform an existing class to a protocol retroactively, is absurdly powerful. It doesn’t offer any privileged manipulation of types today that would give a safety related reason to restrict it. I’d be reluctant to let someone take that away from me personally without a strong language-level justification (such as needing to restrict it partially to meet ABI/resiliency requirements)

-DW

···

On Feb 21, 2017, at 2:27 AM, Joanna Carter <joanna@carterconsulting.org.uk> wrote:


(Slava Pestov) #16

But in the Swift world, we now have the ability to extend almost any type, except Any and AnyObject, which appear to be protected by some deep and dark mechanism within the compiler. So, just as those two protocols cannot be extended, should we not be looking at some generally available mechanism to prevent extensibility of any type?

And, I am not talking visibility here, just extensibility ; somehow those two concerns are often conflated and, I believe, this is the cause of much of the "lively" discussion on visibility specifiers.

This is imho more of an issue with ABI and resiliency. If I can create an extension which adds a method to a type, and that type gains a method with that same signature in a later release, what is the behavior?

Extension methods are lexically scoped and statically dispatched, so existing code will not be affected by the addition of the new method. New code will call whichever method is visible, or if both are visible in a given lexical scope and overload resolution rules cannot pick an unambiguous winner, the code will no longer type check.

The situation is messier with extensions that add protocol conformances. Right now we don’t do a good job of dealing with duplicate conformances because we assume in several places in the compiler and runtime that they can be looked up globally. We plan on addressing at least some of this.

Slava

···

On Feb 21, 2017, at 4:19 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 21, 2017, at 2:27 AM, Joanna Carter <joanna@carterconsulting.org.uk> wrote:

In C++ terms, it would be when I want some other class to have friend access to a function/data, but for it not to be arbitrarily accessible by subtypes

Indeed. I do wonder if some folks approach visibility control as an exercise in "what can I see" whereas, demonstrated by C++ friends, it becomes obvious that it is more about "what do I want to allow to be seen"

Is there not a value in forking this discussion into which keywords are truly about visibility control and which are about extensibility control?

Possibly; they are two axes. However, I’m hoping that new access modifiers (including possible submodule functionality) + extensibility modifiers are considered holistically.

For instance, if there is a feature that allows something comparable to ‘friend’ level access, a restrictive private makes a lot more sense than one with exceptions allowing access to subtypes, within the same file, to extensions, etc. A restrictive, scoped private would be what you use to protect the invariants of your type, with less protected methods given to allow extensions and internal modification safely.

But without a submodule or similar access level, we need a “fileprivate” level access (renamed to ‘private’ or not) to make sure code needing a higher level of access can get it, by being embedded in the same file.

OK, how does this sound?

Extend the 'final' concept, currently used only in classes, to protect any, non-protocol, type from being extended.

The extension mechanism, both being able to add new methods and to conform an existing class to a protocol retroactively, is absurdly powerful. It doesn’t offer any privileged manipulation of types today that would give a safety related reason to restrict it. I’d be reluctant to let someone take that away from me personally without a strong language-level justification (such as needing to restrict it partially to meet ABI/resiliency requirements)

-DW
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joanna Carter) #17

Is there not a value in forking this discussion into which keywords are truly about visibility control and which are about extensibility control?

Possibly; they are two axes. However, I’m hoping that new access modifiers (including possible submodule functionality) + extensibility modifiers are considered holistically.

For instance, if there is a feature that allows something comparable to ‘friend’ level access, a restrictive private makes a lot more sense than one with exceptions allowing access to subtypes, within the same file, to extensions, etc. A restrictive, scoped private would be what you use to protect the invariants of your type, with less protected methods given to allow extensions and internal modification safely.

But without a submodule or similar access level, we need a “fileprivate” level access (renamed to ‘private’ or not) to make sure code needing a higher level of access can get it, by being embedded in the same file.

As you can see, I've already started a thread to try and nail down exactly what we have already got, in an attempt to find where the holes are.

The extension mechanism, both being able to add new methods and to conform an existing class to a protocol retroactively, is absurdly powerful. It doesn’t offer any privileged manipulation of types today that would give a safety related reason to restrict it. I’d be reluctant to let someone take that away from me personally without a strong language-level justification (such as needing to restrict it partially to meet ABI/resiliency requirements)

I am hoping by "starting over" with an analysis of requirements, step by step, that we can clear away some of the confusion that has grown up and lay down agreed requirements for the way ahead.

···

Le 22 févr. 2017 à 01:19, David Waite <david@alkaline-solutions.com> a écrit :

--
Joanna Carter
Carter Consulting


(Goffredo Marocchi) #18

Hey Slava,

But in the Swift world, we now have the ability to extend almost any type, except Any and AnyObject, which appear to be protected by some deep and dark mechanism within the compiler. So, just as those two protocols cannot be extended, should we not be looking at some generally available mechanism to prevent extensibility of any type?

And, I am not talking visibility here, just extensibility ; somehow those two concerns are often conflated and, I believe, this is the cause of much of the "lively" discussion on visibility specifiers.

This is imho more of an issue with ABI and resiliency. If I can create an extension which adds a method to a type, and that type gains a method with that same signature in a later release, what is the behavior?

Extension methods are lexically scoped and statically dispatched, so existing code will not be affected by the addition of the new method. New code will call whichever method is visible, or if both are visible in a given lexical scope and overload resolution rules cannot pick an unambiguous winner, the code will no longer type check.

This situation seems pretty messy as, say with a binary framework one day, it may lead to someone conforming to a protocol and implementing a method that will never be called as it is silently overriding a default method only declared in an extension... and there are no warnings for that.
I think that if the protocol or a protocol extension provides a default implementation a class conforming to it not being allowed to override it may actually help.

Not to mention how for many protocols should be abstract contracts not implementations, I cannot be the only one that enjoys returning to a situation where the code being executed depends on how I dress my instance and not what my instance is:
...objA is an instance of ClassA that conforms to ProtoA which also has a myMethod method and an extension which provides a default implementation of it.

objA.myMethod()

vs

(objA as ProtoA).myMethod()

When did casting an instance to another type changes what code gets executed became ok and good practice again :)?

Default methods are serving a purpose and are allowing code sharing across structs, but IMHO they cause more harm than good with classes and OOP design while not being a necessary condition for POP either.

···

On 22 Feb 2017, at 23:07, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 21, 2017, at 4:19 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 21, 2017, at 2:27 AM, Joanna Carter <joanna@carterconsulting.org.uk> wrote:

The situation is messier with extensions that add protocol conformances. Right now we don’t do a good job of dealing with duplicate conformances because we assume in several places in the compiler and runtime that they can be looked up globally. We plan on addressing at least some of this.

Slava

In C++ terms, it would be when I want some other class to have friend access to a function/data, but for it not to be arbitrarily accessible by subtypes

Indeed. I do wonder if some folks approach visibility control as an exercise in "what can I see" whereas, demonstrated by C++ friends, it becomes obvious that it is more about "what do I want to allow to be seen"

Is there not a value in forking this discussion into which keywords are truly about visibility control and which are about extensibility control?

Possibly; they are two axes. However, I’m hoping that new access modifiers (including possible submodule functionality) + extensibility modifiers are considered holistically.

For instance, if there is a feature that allows something comparable to ‘friend’ level access, a restrictive private makes a lot more sense than one with exceptions allowing access to subtypes, within the same file, to extensions, etc. A restrictive, scoped private would be what you use to protect the invariants of your type, with less protected methods given to allow extensions and internal modification safely.

But without a submodule or similar access level, we need a “fileprivate” level access (renamed to ‘private’ or not) to make sure code needing a higher level of access can get it, by being embedded in the same file.

OK, how does this sound?

Extend the 'final' concept, currently used only in classes, to protect any, non-protocol, type from being extended.

The extension mechanism, both being able to add new methods and to conform an existing class to a protocol retroactively, is absurdly powerful. It doesn’t offer any privileged manipulation of types today that would give a safety related reason to restrict it. I’d be reluctant to let someone take that away from me personally without a strong language-level justification (such as needing to restrict it partially to meet ABI/resiliency requirements)

-DW
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution