[Review] SE-0117: Default classes to be non-subclassable publicly

Please stop saying that this proposal will bring more consideration to the design of libraries. It isn’t true. I haven’t even seen an argument for why it would be true, it is just taken for granted that it is true.

As I mentioned in another post, this is structurally very different from things like ‘if-let’ and optionals. Optionals force the user to consider their decision in the context it is being used (i.e. as you use the optional/value). This proposal, however, does the opposite. The effect of your actions appear in the context of a completely different user. It is like sitting in a room, flipping a light switch wondering “I wonder what this does?”… meanwhile the people downstairs are wondering why their lights keep turning off and on”.

You can try to test for this, but by definition library authors can only test for scenarios that they have thought of. I have often found people surprise me with their use-cases. Relying on the diligence of other programmers is what leads to things like: "You always need to remember to test for zero before using a pointer". Literally the opposite of optionals! It sounds good, but at the end of the day, people are human and they WILL make mistakes. Best to either catch those mistakes in the context where they happen or to mitigate the effect of it. This proposal basically forces you to feel the full effect of other people's mistakes (thinking that it will discourage them from making them in the first place).

Your only real mechanism for feedback is when users of your library complain to you that something that they need isn’t subclass-able. This is the point where most framework authors will actually learn that this feature/default exists. Users of a framework will learn of it slightly earlier, when they find they need to subclass something, and it just isn’t possible.

I would much prefer adding a ‘sealed’ keyword which library authors could use to annotate things which they do not want subclassed outside of the module. Or preferably, as others have suggested, allow augmentation of ‘final’ with ‘public(final)' or ‘internal(final)’.

The only case where I would support ‘sealed’ by default is if there are 3 levels: open, sealed, final. Final would allow 'public(final)' and 'internal(final)’ to allow private subclassing inside the file/module. Sealed would be the same, except it would allow the user to subclass by explicitly acknowledging the risk using ‘unsafe’: “unsafe class MySubclass:SealedSuper“ and “unsafe override func”. Final would not allow the override.

That is the case where ‘sealed’ makes sense as a default…

Thanks,
Jon

P.S. The current proposal will only cause massive problems down the line, IMHO. We will find an escape hatch is needed, but we will have made optimizations based on assumptions of finality which prevent us from easily adding one.

I personally agree with most of your assessments. It's why I pushed so hard for "allow subclassing my default" in the first discussion of this point.

The problem with this is simple: you cannot retroactively "close up" an API. I cannot add final to a class I have previously declared as non-final. I also can seal a class which has previously been open to subclassing.

Consider: someone builds against my framework and I do nothing, and they subclass my classes. Then later I come through and mark the classes as "Sealed". What should we do with those classes that are subclassing my classes? Nothing. I can't. I permitted access and now I'm beholden to that access level.

On the other hand, opening up access levels gradually has no such issues. Users of my class can't subclass, and then they can. They just have another tool in the bag now.

If you want a default, it should be one you can reverse later. Your default should not be the most restrictive.

Whilst I agree with most of your points, this core concept seems to trump them to my mind.

- Rod

···

On 10 Jul. 2016, at 5:51 am, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Please stop saying that this proposal will bring more consideration to the design of libraries. It isn’t true. I haven’t even seen an argument for why it would be true, it is just taken for granted that it is true.

As I mentioned in another post, this is structurally very different from things like ‘if-let’ and optionals. Optionals force the user to consider their decision in the context it is being used (i.e. as you use the optional/value). This proposal, however, does the opposite. The effect of your actions appear in the context of a completely different user. It is like sitting in a room, flipping a light switch wondering “I wonder what this does?”… meanwhile the people downstairs are wondering why their lights keep turning off and on”.

You can try to test for this, but by definition library authors can only test for scenarios that they have thought of. I have often found people surprise me with their use-cases. Relying on the diligence of other programmers is what leads to things like: "You always need to remember to test for zero before using a pointer". Literally the opposite of optionals! It sounds good, but at the end of the day, people are human and they WILL make mistakes. Best to either catch those mistakes in the context where they happen or to mitigate the effect of it. This proposal basically forces you to feel the full effect of other people's mistakes (thinking that it will discourage them from making them in the first place).

Your only real mechanism for feedback is when users of your library complain to you that something that they need isn’t subclass-able. This is the point where most framework authors will actually learn that this feature/default exists. Users of a framework will learn of it slightly earlier, when they find they need to subclass something, and it just isn’t possible.

I would much prefer adding a ‘sealed’ keyword which library authors could use to annotate things which they do not want subclassed outside of the module. Or preferably, as others have suggested, allow augmentation of ‘final’ with ‘public(final)' or ‘internal(final)’.

The only case where I would support ‘sealed’ by default is if there are 3 levels: open, sealed, final. Final would allow 'public(final)' and 'internal(final)’ to allow private subclassing inside the file/module. Sealed would be the same, except it would allow the user to subclass by explicitly acknowledging the risk using ‘unsafe’: “unsafe class MySubclass:SealedSuper“ and “unsafe override func”. Final would not allow the override.

That is the case where ‘sealed’ makes sense as a default…

Thanks,
Jon

P.S. The current proposal will only cause massive problems down the line, IMHO. We will find an escape hatch is needed, but we will have made optimizations based on assumptions of finality which prevent us from easily adding one.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

@Rod: Thank you for actually replying to the content of my post. Much appreciated.

It is a trolly problem. You are arguing that pre-breaking everyone's code is better (even if causes way more trouble overall) than taking an action that breaks a few people’s code later (and thus feeling responsible). There are other options. I grew up watching enough Star Trek that I don’t believe in no win scenarios.

I still think my compromise is the best solution. 3 levels: Open, Sealed, Final. The difference is that sealed can be overridden with a compiler warning and use of “unsafe” keyword, but final can’t be overridden. That way the user is acknowledging that they are doing something which isn’t supported in the context which they are doing it… but it doesn’t completely prevent it by default. Opt-out safety. (Yes you lose some compiler optimizations in the default case, but that was premature optimization anyway).

As for the interaction with public/internal/etc… that should be explicit. This proposal confuses and intermingles them. You should just be able to say ‘public final internal(open)’ or some other syntax which lets you express the same sentiment. (The above says the class/method is public and final, but internally it is open.)

I am not saying that we shouldn’t solve these issues. I am saying this proposal:
1) Isn’t easily discoverable.
2) Requires communication between different parties (author and user) which slows iteration cycles to weeks or months (vs the usual minutes)
3) Conflates/mixes the ideas of access level and subclass-ability
4) Makes way too many separate changes in a single proposal: New idea of “sealed”, new default, replaces ‘public’ in some cases but not others, new keywords (which everyone wants to change), etc…
5) Has structural problems which mean that it won’t actually increase thoughtfulness
6) Makes app developers extremely dependent on framework authors (and those author’s schedules)
7) Will require us to patch/fix it later, but that will be difficult due to optimizations/assumptions of finality.
8) Will cause unnecessary pain for both framework authors and users of frameworks

We would be foolish to accept this proposal without planning for the inevitable escape hatch. We will need it, and if we don’t plan for it, it will break everything when we are forced to fix it in Swift 4/5. Anything else is idealistic to the point of ignoring real-world use/behavior.

As I said before, this type of thinking: “If we just make things more difficult, it will encourage awareness” is what leads to the DMV, TSA, and Java. The problem is, the brain doesn’t work that way, and it ultimately just adds pain without being effective. You can add forcing functions (like optional unwrapping), which are annoying, but effective… but as I also mentioned before this proposal doesn’t do that. It is structurally different. It will not do what you think it does.

Thanks,
Jon

P.S. There is also a dangerous difference between helping the programmer catch mistakes (e.g. don’t accidentally subclass the wrong method) and trying to prevent them from coding in a style you disagree with. I have been seeing far to many proposals of the second variety of late.

···

On Jul 10, 2016, at 2:58 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

I personally agree with most of your assessments. It's why I pushed so hard for "allow subclassing my default" in the first discussion of this point.

The problem with this is simple: you cannot retroactively "close up" an API. I cannot add final to a class I have previously declared as non-final. I also can seal a class which has previously been open to subclassing.

Consider: someone builds against my framework and I do nothing, and they subclass my classes. Then later I come through and mark the classes as "Sealed". What should we do with those classes that are subclassing my classes? Nothing. I can't. I permitted access and now I'm beholden to that access level.

On the other hand, opening up access levels gradually has no such issues. Users of my class can't subclass, and then they can. They just have another tool in the bag now.

If you want a default, it should be one you can reverse later. Your default should not be the most restrictive.

Whilst I agree with most of your points, this core concept seems to trump them to my mind.

- Rod

On 10 Jul. 2016, at 5:51 am, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Please stop saying that this proposal will bring more consideration to the design of libraries. It isn’t true. I haven’t even seen an argument for why it would be true, it is just taken for granted that it is true.

As I mentioned in another post, this is structurally very different from things like ‘if-let’ and optionals. Optionals force the user to consider their decision in the context it is being used (i.e. as you use the optional/value). This proposal, however, does the opposite. The effect of your actions appear in the context of a completely different user. It is like sitting in a room, flipping a light switch wondering “I wonder what this does?”… meanwhile the people downstairs are wondering why their lights keep turning off and on”.

You can try to test for this, but by definition library authors can only test for scenarios that they have thought of. I have often found people surprise me with their use-cases. Relying on the diligence of other programmers is what leads to things like: "You always need to remember to test for zero before using a pointer". Literally the opposite of optionals! It sounds good, but at the end of the day, people are human and they WILL make mistakes. Best to either catch those mistakes in the context where they happen or to mitigate the effect of it. This proposal basically forces you to feel the full effect of other people's mistakes (thinking that it will discourage them from making them in the first place).

Your only real mechanism for feedback is when users of your library complain to you that something that they need isn’t subclass-able. This is the point where most framework authors will actually learn that this feature/default exists. Users of a framework will learn of it slightly earlier, when they find they need to subclass something, and it just isn’t possible.

I would much prefer adding a ‘sealed’ keyword which library authors could use to annotate things which they do not want subclassed outside of the module. Or preferably, as others have suggested, allow augmentation of ‘final’ with ‘public(final)' or ‘internal(final)’.

The only case where I would support ‘sealed’ by default is if there are 3 levels: open, sealed, final. Final would allow 'public(final)' and 'internal(final)’ to allow private subclassing inside the file/module. Sealed would be the same, except it would allow the user to subclass by explicitly acknowledging the risk using ‘unsafe’: “unsafe class MySubclass:SealedSuper“ and “unsafe override func”. Final would not allow the override.

That is the case where ‘sealed’ makes sense as a default…

Thanks,
Jon

P.S. The current proposal will only cause massive problems down the line, IMHO. We will find an escape hatch is needed, but we will have made optimizations based on assumptions of finality which prevent us from easily adding one.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

The problem with this is simple: you cannot retroactively "close up" an API. I cannot add final to a class I have previously declared as non-final. I also can seal a class which has previously been open to subclassing.

Of course you can do both — it may make users angry, but so what?
The essence of this way of thinking is "I fear the reaction of my users when I take something away from them… so I have to deny them those options right from start".

I've no statistics (there's a general lack of facts all over the place…), but I don't thing the majority of todays and future libraries are build with a strictly planned top-down approach and guarantees about API-stability. Nowadays things are much more spontaneous, and the strict rules and limits some people here want to force upon others would most likely decrease the joy in playing with the language and start experiments in it.

There is only a very small number of Swift-developers writing frameworks that are used on a large scale basis, and even if many people in this group vote for limiting defaults, the focus should be on the majority:
When I come to the conclusion that a set of classes in a project could be useful somewhere else, my problem is not fear of future API changes — it is the daunting task of having to sprinkle "public" all over the place.
Sealing classes makes proper code reuse a more tedious job, and I want Swift to stay fun rather than become a playground for sticklers for order.

This is the case with a library.

It is *not* the case with a framework. Dynamically linked frameworks can be changed without the app even being changed. Imagine if Apple changed UIKit and make UINavigationController final retroactively. UIKit internally would therefore call directly to the UINavigationController methods statically, and bypass any subclass’s methods, thereby breaking applications. Should app apps have to be rebuilt to work with iOS 12 simply because Apple shipped a new version of UIKit? This is illogical. You will *break* things by finalising after the fact. Swift, being about safety, needs to deal with this safely.

···

On 11 Jul 2016, at 9:13 AM, Tino Heth <2th@gmx.de> wrote:

The problem with this is simple: you cannot retroactively "close up" an API. I cannot add final to a class I have previously declared as non-final. I also can seal a class which has previously been open to subclassing.

Of course you can do both — it may make users angry, but so what?
The essence of this way of thinking is "I fear the reaction of my users when I take something away from them… so I have to deny them those options right from start".

I've no statistics (there's a general lack of facts all over the place…), but I don't thing the majority of todays and future libraries are build with a strictly planned top-down approach and guarantees about API-stability. Nowadays things are much more spontaneous, and the strict rules and limits some people here want to force upon others would most likely decrease the joy in playing with the language and start experiments in it.

There is only a very small number of Swift-developers writing frameworks that are used on a large scale basis, and even if many people in this group vote for limiting defaults, the focus should be on the majority:
When I come to the conclusion that a set of classes in a project could be useful somewhere else, my problem is not fear of future API changes — it is the daunting task of having to sprinkle "public" all over the place.
Sealing classes makes proper code reuse a more tedious job, and I want Swift to stay fun rather than become a playground for sticklers for order.

@Jonathan

I don’t think that "pre-breaking code" is a good description. You are not breaking anything - you’re just not allowing something that *could* become unsafe. It’s safety first, at the cost of the library user’s flexibility.

That said, I actually think you have a good point however that “sealed” should be able to be overridden, either in a patch capacity or an “unsafe” capacity. Should this become final at a later point, you have acknowledged you know this will be unsafe and are willing to take this risk to get the job done. This is opt-in risk.

Perhaps however this shouldn’t be “sealed” declaratively. Perhaps we just have a keyword for “Open” as an access level, and if you subclass or override things that are not “open” from other modules, you must mark unsafe.

I think this is a decent compromise: We allow potential to patch, but discourage without acknowledgement of the risk. Allow Final and Open to be declarative.

- Rod

···

On 11 Jul 2016, at 11:05 AM, Jonathan Hull <jhull@gbis.com> wrote:

@Rod: Thank you for actually replying to the content of my post. Much appreciated.

It is a trolly problem. You are arguing that pre-breaking everyone's code is better (even if causes way more trouble overall) than taking an action that breaks a few people’s code later (and thus feeling responsible). There are other options. I grew up watching enough Star Trek that I don’t believe in no win scenarios.

I still think my compromise is the best solution. 3 levels: Open, Sealed, Final. The difference is that sealed can be overridden with a compiler warning and use of “unsafe” keyword, but final can’t be overridden. That way the user is acknowledging that they are doing something which isn’t supported in the context which they are doing it… but it doesn’t completely prevent it by default. Opt-out safety. (Yes you lose some compiler optimizations in the default case, but that was premature optimization anyway).

As for the interaction with public/internal/etc… that should be explicit. This proposal confuses and intermingles them. You should just be able to say ‘public final internal(open)’ or some other syntax which lets you express the same sentiment. (The above says the class/method is public and final, but internally it is open.)

I am not saying that we shouldn’t solve these issues. I am saying this proposal:
1) Isn’t easily discoverable.
2) Requires communication between different parties (author and user) which slows iteration cycles to weeks or months (vs the usual minutes)
3) Conflates/mixes the ideas of access level and subclass-ability
4) Makes way too many separate changes in a single proposal: New idea of “sealed”, new default, replaces ‘public’ in some cases but not others, new keywords (which everyone wants to change), etc…
5) Has structural problems which mean that it won’t actually increase thoughtfulness
6) Makes app developers extremely dependent on framework authors (and those author’s schedules)
7) Will require us to patch/fix it later, but that will be difficult due to optimizations/assumptions of finality.
8) Will cause unnecessary pain for both framework authors and users of frameworks

We would be foolish to accept this proposal without planning for the inevitable escape hatch. We will need it, and if we don’t plan for it, it will break everything when we are forced to fix it in Swift 4/5. Anything else is idealistic to the point of ignoring real-world use/behavior.

As I said before, this type of thinking: “If we just make things more difficult, it will encourage awareness” is what leads to the DMV, TSA, and Java. The problem is, the brain doesn’t work that way, and it ultimately just adds pain without being effective. You can add forcing functions (like optional unwrapping), which are annoying, but effective… but as I also mentioned before this proposal doesn’t do that. It is structurally different. It will not do what you think it does.

Thanks,
Jon

P.S. There is also a dangerous difference between helping the programmer catch mistakes (e.g. don’t accidentally subclass the wrong method) and trying to prevent them from coding in a style you disagree with. I have been seeing far to many proposals of the second variety of late.

On Jul 10, 2016, at 2:58 PM, Rod Brown <rodney.brown6@icloud.com <mailto:rodney.brown6@icloud.com>> wrote:

I personally agree with most of your assessments. It's why I pushed so hard for "allow subclassing my default" in the first discussion of this point.

The problem with this is simple: you cannot retroactively "close up" an API. I cannot add final to a class I have previously declared as non-final. I also can seal a class which has previously been open to subclassing.

Consider: someone builds against my framework and I do nothing, and they subclass my classes. Then later I come through and mark the classes as "Sealed". What should we do with those classes that are subclassing my classes? Nothing. I can't. I permitted access and now I'm beholden to that access level.

On the other hand, opening up access levels gradually has no such issues. Users of my class can't subclass, and then they can. They just have another tool in the bag now.

If you want a default, it should be one you can reverse later. Your default should not be the most restrictive.

Whilst I agree with most of your points, this core concept seems to trump them to my mind.

- Rod

On 10 Jul. 2016, at 5:51 am, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Please stop saying that this proposal will bring more consideration to the design of libraries. It isn’t true. I haven’t even seen an argument for why it would be true, it is just taken for granted that it is true.

As I mentioned in another post, this is structurally very different from things like ‘if-let’ and optionals. Optionals force the user to consider their decision in the context it is being used (i.e. as you use the optional/value). This proposal, however, does the opposite. The effect of your actions appear in the context of a completely different user. It is like sitting in a room, flipping a light switch wondering “I wonder what this does?”… meanwhile the people downstairs are wondering why their lights keep turning off and on”.

You can try to test for this, but by definition library authors can only test for scenarios that they have thought of. I have often found people surprise me with their use-cases. Relying on the diligence of other programmers is what leads to things like: "You always need to remember to test for zero before using a pointer". Literally the opposite of optionals! It sounds good, but at the end of the day, people are human and they WILL make mistakes. Best to either catch those mistakes in the context where they happen or to mitigate the effect of it. This proposal basically forces you to feel the full effect of other people's mistakes (thinking that it will discourage them from making them in the first place).

Your only real mechanism for feedback is when users of your library complain to you that something that they need isn’t subclass-able. This is the point where most framework authors will actually learn that this feature/default exists. Users of a framework will learn of it slightly earlier, when they find they need to subclass something, and it just isn’t possible.

I would much prefer adding a ‘sealed’ keyword which library authors could use to annotate things which they do not want subclassed outside of the module. Or preferably, as others have suggested, allow augmentation of ‘final’ with ‘public(final)' or ‘internal(final)’.

The only case where I would support ‘sealed’ by default is if there are 3 levels: open, sealed, final. Final would allow 'public(final)' and 'internal(final)’ to allow private subclassing inside the file/module. Sealed would be the same, except it would allow the user to subclass by explicitly acknowledging the risk using ‘unsafe’: “unsafe class MySubclass:SealedSuper“ and “unsafe override func”. Final would not allow the override.

That is the case where ‘sealed’ makes sense as a default…

Thanks,
Jon

P.S. The current proposal will only cause massive problems down the line, IMHO. We will find an escape hatch is needed, but we will have made optimizations based on assumptions of finality which prevent us from easily adding one.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

@Jonathan

I don’t think that "pre-breaking code" is a good description. You are not breaking anything - you’re just not allowing something that *could* become unsafe. It’s safety first, at the cost of the library user’s flexibility.

It is pre-breaking in that it is the exact same code that doesn’t work in both cases (only in the pre-breaking case, a bunch of other code also doesn’t work). I know it feels different because it “was never possible” vs our change being the cause, but it is one of those things like me giving you $5 or giving you $10 and later taking back $5. As humans we are loss averse so we devise strategies to hide the loss from ourselves.

That said, I actually think you have a good point however that “sealed” should be able to be overridden, either in a patch capacity or an “unsafe” capacity. Should this become final at a later point, you have acknowledged you know this will be unsafe and are willing to take this risk to get the job done. This is opt-in risk.

Perhaps however this shouldn’t be “sealed” declaratively. Perhaps we just have a keyword for “Open” as an access level, and if you subclass or override things that are not “open” from other modules, you must mark unsafe.

I think this is a decent compromise: We allow potential to patch, but discourage without acknowledgement of the risk. Allow Final and Open to be declarative.

Pretty much, but it is important IMHO to separate this from access levels. They need to be orthogonal concepts or it will be confusing (e.g. why are structs “public” but classes “open”?).

The default in that case would actually be ‘sealed internal(open)’ which would suck to type repeatedly, but you won’t have to because it is the default. The thing you would type often would be ‘public open’, which is shorter than the new keywords in the proposal (and consistent for both classes and methods instead of learning different words)

I think we basically agree overall.

Thanks,
Jon

···

On Jul 10, 2016, at 6:45 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

- Rod

On 11 Jul 2016, at 11:05 AM, Jonathan Hull <jhull@gbis.com <mailto:jhull@gbis.com>> wrote:

@Rod: Thank you for actually replying to the content of my post. Much appreciated.

It is a trolly problem. You are arguing that pre-breaking everyone's code is better (even if causes way more trouble overall) than taking an action that breaks a few people’s code later (and thus feeling responsible). There are other options. I grew up watching enough Star Trek that I don’t believe in no win scenarios.

I still think my compromise is the best solution. 3 levels: Open, Sealed, Final. The difference is that sealed can be overridden with a compiler warning and use of “unsafe” keyword, but final can’t be overridden. That way the user is acknowledging that they are doing something which isn’t supported in the context which they are doing it… but it doesn’t completely prevent it by default. Opt-out safety. (Yes you lose some compiler optimizations in the default case, but that was premature optimization anyway).

As for the interaction with public/internal/etc… that should be explicit. This proposal confuses and intermingles them. You should just be able to say ‘public final internal(open)’ or some other syntax which lets you express the same sentiment. (The above says the class/method is public and final, but internally it is open.)

I am not saying that we shouldn’t solve these issues. I am saying this proposal:
1) Isn’t easily discoverable.
2) Requires communication between different parties (author and user) which slows iteration cycles to weeks or months (vs the usual minutes)
3) Conflates/mixes the ideas of access level and subclass-ability
4) Makes way too many separate changes in a single proposal: New idea of “sealed”, new default, replaces ‘public’ in some cases but not others, new keywords (which everyone wants to change), etc…
5) Has structural problems which mean that it won’t actually increase thoughtfulness
6) Makes app developers extremely dependent on framework authors (and those author’s schedules)
7) Will require us to patch/fix it later, but that will be difficult due to optimizations/assumptions of finality.
8) Will cause unnecessary pain for both framework authors and users of frameworks

We would be foolish to accept this proposal without planning for the inevitable escape hatch. We will need it, and if we don’t plan for it, it will break everything when we are forced to fix it in Swift 4/5. Anything else is idealistic to the point of ignoring real-world use/behavior.

As I said before, this type of thinking: “If we just make things more difficult, it will encourage awareness” is what leads to the DMV, TSA, and Java. The problem is, the brain doesn’t work that way, and it ultimately just adds pain without being effective. You can add forcing functions (like optional unwrapping), which are annoying, but effective… but as I also mentioned before this proposal doesn’t do that. It is structurally different. It will not do what you think it does.

Thanks,
Jon

P.S. There is also a dangerous difference between helping the programmer catch mistakes (e.g. don’t accidentally subclass the wrong method) and trying to prevent them from coding in a style you disagree with. I have been seeing far to many proposals of the second variety of late.

On Jul 10, 2016, at 2:58 PM, Rod Brown <rodney.brown6@icloud.com <mailto:rodney.brown6@icloud.com>> wrote:

I personally agree with most of your assessments. It's why I pushed so hard for "allow subclassing my default" in the first discussion of this point.

The problem with this is simple: you cannot retroactively "close up" an API. I cannot add final to a class I have previously declared as non-final. I also can seal a class which has previously been open to subclassing.

Consider: someone builds against my framework and I do nothing, and they subclass my classes. Then later I come through and mark the classes as "Sealed". What should we do with those classes that are subclassing my classes? Nothing. I can't. I permitted access and now I'm beholden to that access level.

On the other hand, opening up access levels gradually has no such issues. Users of my class can't subclass, and then they can. They just have another tool in the bag now.

If you want a default, it should be one you can reverse later. Your default should not be the most restrictive.

Whilst I agree with most of your points, this core concept seems to trump them to my mind.

- Rod

On 10 Jul. 2016, at 5:51 am, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Please stop saying that this proposal will bring more consideration to the design of libraries. It isn’t true. I haven’t even seen an argument for why it would be true, it is just taken for granted that it is true.

As I mentioned in another post, this is structurally very different from things like ‘if-let’ and optionals. Optionals force the user to consider their decision in the context it is being used (i.e. as you use the optional/value). This proposal, however, does the opposite. The effect of your actions appear in the context of a completely different user. It is like sitting in a room, flipping a light switch wondering “I wonder what this does?”… meanwhile the people downstairs are wondering why their lights keep turning off and on”.

You can try to test for this, but by definition library authors can only test for scenarios that they have thought of. I have often found people surprise me with their use-cases. Relying on the diligence of other programmers is what leads to things like: "You always need to remember to test for zero before using a pointer". Literally the opposite of optionals! It sounds good, but at the end of the day, people are human and they WILL make mistakes. Best to either catch those mistakes in the context where they happen or to mitigate the effect of it. This proposal basically forces you to feel the full effect of other people's mistakes (thinking that it will discourage them from making them in the first place).

Your only real mechanism for feedback is when users of your library complain to you that something that they need isn’t subclass-able. This is the point where most framework authors will actually learn that this feature/default exists. Users of a framework will learn of it slightly earlier, when they find they need to subclass something, and it just isn’t possible.

I would much prefer adding a ‘sealed’ keyword which library authors could use to annotate things which they do not want subclassed outside of the module. Or preferably, as others have suggested, allow augmentation of ‘final’ with ‘public(final)' or ‘internal(final)’.

The only case where I would support ‘sealed’ by default is if there are 3 levels: open, sealed, final. Final would allow 'public(final)' and 'internal(final)’ to allow private subclassing inside the file/module. Sealed would be the same, except it would allow the user to subclass by explicitly acknowledging the risk using ‘unsafe’: “unsafe class MySubclass:SealedSuper“ and “unsafe override func”. Final would not allow the override.

That is the case where ‘sealed’ makes sense as a default…

Thanks,
Jon

P.S. The current proposal will only cause massive problems down the line, IMHO. We will find an escape hatch is needed, but we will have made optimizations based on assumptions of finality which prevent us from easily adding one.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

It is *not* the case with a framework. Dynamically linked frameworks can be changed without the app even being changed. Imagine if Apple changed UIKit and make UINavigationController final retroactively.

Your argument is true, and you choose a good example — but it's actually the only one that exists at all:
There are no other parties that ship frameworks that way, and I don't think this should change.

So the benefits of this proposal would exist for Apple alone, and I can understand why sealed is convenient for an UIKit-engineer.
It is tempting to aim for the easiest solution, but it is smarter to take the route that's better for the customer (and we happen to be the customers).
And, as others pointed out before, Cupertino is free to diverge from the defaults in either direction, so it is only a little effort to have "sealed by default" without the bad side effects.

Finally: Someone from the other party who not only speaks about compromise, but also shares a compatible way of thinking :-)
All those strict rules are a pointless attempt to trade freedom for security, and are purely cosmetic in most cases: In the context of open source, they are a blunt sword, whose only power is to annoy users.
Instead of trying to avoid all possible problems users of your library may run into, it's better to treat them as adults who know what they are doing — and make sure that the actually know what they are doing.

The defaults should match reality, and that is neither "overriding is trouble" nor "it's safe to subclass"; it is "I haven't thought about overriding yet".
There is even a natural choice for the syntax to acknowledge that you are aware of doing something that might be dangerous: Simply add an exclamation mark to override.

Tino

···

Am 11.07.2016 um 03:45 schrieb Rod Brown <rodney.brown6@icloud.com>:

That said, I actually think you have a good point however that “sealed” should be able to be overridden, either in a patch capacity or an “unsafe” capacity. Should this become final at a later point, you have acknowledged you know this will be unsafe and are willing to take this risk to get the job done. This is opt-in risk.

Perhaps however this shouldn’t be “sealed” declaratively. Perhaps we just have a keyword for “Open” as an access level, and if you subclass or override things that are not “open” from other modules, you must mark unsafe.

I think this is a decent compromise: We allow potential to patch, but discourage without acknowledgement of the risk. Allow Final and Open to be declarative.

I completely disagree with this.

Not providing someone something due to risk of breakage is not the same thing as “giving it and taking it away”. We don’t build bridges out of spare parts and tell people “We build it but we expect it to break at some stage, so use with caution.” You either build it correctly, or you don’t let people cross the bridge. At All.

This isn’t about “loss averse”. This is about risk management.

Where does the line lie on risk? That’s ultimately something the core team will have to decide.

···

On 11 Jul 2016, at 12:33 PM, Jonathan Hull <jhull@gbis.com> wrote:

It is pre-breaking in that it is the exact same code that doesn’t work in both cases (only in the pre-breaking case, a bunch of other code also doesn’t work). I know it feels different because it “was never possible” vs our change being the cause, but it is one of those things like me giving you $5 or giving you $10 and later taking back $5. As humans we are loss averse so we devise strategies to hide the loss from ourselves.

Resent for Swift Evolution:

With the existence of Swift on the server, dynamically linked, independently distributed frameworks will not be an Apple-only issue - this extends beyond Apple's OS X-based platforms towards how dynamic frameworks link against each other as if they are to be distributed separately.

It is short sighted to suggest that all Swift deployments will be under Apple's control.

···

On 11 Jul. 2016, at 3:43 pm, Tino Heth <2th@gmx.de> wrote:

It is *not* the case with a framework. Dynamically linked frameworks can be changed without the app even being changed. Imagine if Apple changed UIKit and make UINavigationController final retroactively.

Your argument is true, and you choose a good example — but it's actually the only one that exists at all:
There are no other parties that ship frameworks that way, and I don't think this should change.

So the benefits of this proposal would exist for Apple alone, and I can understand why sealed is convenient for an UIKit-engineer.
It is tempting to aim for the easiest solution, but it is smarter to take the route that's better for the customer (and we happen to be the customers).
And, as others pointed out before, Cupertino is free to diverge from the defaults in either direction, so it is only a little effort to have "sealed by default" without the bad side effects.

Is it more unreasonable to ask developers to seal their modules or users to unseal them? I would say Apple or third parties shipping frameworks could be asked to think about subclass ability before shipping their commercial library.

···

Sent from my iPhone

On 11 Jul 2016, at 07:36, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:

Resent for Swift Evolution:

With the existence of Swift on the server, dynamically linked, independently distributed frameworks will not be an Apple-only issue - this extends beyond Apple's OS X-based platforms towards how dynamic frameworks link against each other as if they are to be distributed separately.

It is short sighted to suggest that all Swift deployments will be under Apple's control.

On 11 Jul. 2016, at 3:43 pm, Tino Heth <2th@gmx.de> wrote:

It is *not* the case with a framework. Dynamically linked frameworks can be changed without the app even being changed. Imagine if Apple changed UIKit and make UINavigationController final retroactively.

Your argument is true, and you choose a good example — but it's actually the only one that exists at all:
There are no other parties that ship frameworks that way, and I don't think this should change.

So the benefits of this proposal would exist for Apple alone, and I can understand why sealed is convenient for an UIKit-engineer.
It is tempting to aim for the easiest solution, but it is smarter to take the route that's better for the customer (and we happen to be the customers).
And, as others pointed out before, Cupertino is free to diverge from the defaults in either direction, so it is only a little effort to have "sealed by default" without the bad side effects.

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

With the existence of Swift on the server, dynamically linked, independently distributed frameworks will not be an Apple-only issue - this extends beyond Apple's OS X-based platforms towards how dynamic frameworks link against each other as if they are to be distributed separately.

It is short sighted to suggest that all Swift deployments will be under Apple's control.

I'm really looking forward for server-side Swift — I'm planning for years to extend my portfolio in that direction, and Swift could really push that diversification.

But I had a concrete reason for interest in writing my own backend-code:
Server-side was imho broken on large scale, and it still isn't fixed yet… I can run circles around those poor Java-developers who have to fight crusted structures and deal with sluggish tools like Maven and Tomcat (and Java ;-).*
It seems to me I'm not alone with my opinion, because there are already alternatives on the rise:
Look at Docker — it's a huge success, because it not only takes application and libraries to build a robust unit; it even includes a whole OS!

On iOS, it already hurts when you have a bunch of Swift-Apps which all have the stdlib bundled — but on the server, this doesn't matter, and I'm convinced it would be a bad move to propagate shared frameworks.

- Tino

* of course, there are agile alternatives — but in my environment, most of the big players wouldn't even consider something like Rails

Regards
(From mobile)

With the existence of Swift on the server, dynamically linked, independently distributed frameworks will not be an Apple-only issue - this extends beyond Apple's OS X-based platforms towards how dynamic frameworks link against each other as if they are to be distributed separately.

It is short sighted to suggest that all Swift deployments will be under Apple's control.

I'm really looking forward for server-side Swift — I'm planning for years to extend my portfolio in that direction, and Swift could really push that diversification.

But I had a concrete reason for interest in writing my own backend-code:
Server-side was imho broken on large scale, and it still isn't fixed yet… I can run circles around those poor Java-developers who have to fight crusted structures and deal with sluggish tools like Maven and Tomcat (and Java ;-).*

You do realize that then itunes store used to (i don't know hese days) for many years run on java, despite objc being such a more advanced environment. ;-)

···

On Jul 11, 2016, at 9:39 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

It seems to me I'm not alone with my opinion, because there are already alternatives on the rise:
Look at Docker — it's a huge success, because it not only takes application and libraries to build a robust unit; it even includes a whole OS!

On iOS, it already hurts when you have a bunch of Swift-Apps which all have the stdlib bundled — but on the server, this doesn't matter, and I'm convinced it would be a bad move to propagate shared frameworks.

- Tino

* of course, there are agile alternatives — but in my environment, most of the big players wouldn't even consider something like Rails

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

You do realize that then itunes store used to (i don't know hese days) for many years run on java, despite objc being such a more advanced environment. ;-)

well, from my experience, app developers are running circles around the itunes store all the time ;-)

My point is that you are completely ignoring an entire class of risk that has a real-world $$$ cost. Every time I have to use a framework under this proposal, I am now completely at the mercy of the author. In the case of open source frameworks I can at least make a fork, but for closed source frameworks (often from large companies where us using their framework has been negotiated by the bosses) you have now just added weeks to my development cycle while I wait for big-company-who-doesn’t-really-care-that-much to update their stuff. (sure I can work on other things during that time, but delaying a launch isn’t free)

Are you offering to explain to my boss/client why I can’t add the feature in a reasonable timeframe like I can with Objective C frameworks? That it may not even be possible now in Swift even though the Android guy just did it in a few hours?

Do you know what I am going to tell my boss/client? "Don’t use Swift for frameworks” and “Try to avoid partnering with companies that have Swift frameworks”. "It is too much of a risk". "We are giving them too much control over the future of our product…" I mean, it even affects the prices that companies can charge for crappy frameworks. If I am asking for a bunch of features that I NEED them to add to provide a basic feature, that affects negotiations/price (vs a world where I can add it myself if needed). Sealed-by-default gives them leverage.

To use your bridge analogy, which is better in the case that you haven’t provided a bridge for me:
1) I build my own bridge knowing that I will need to maintain it periodically (usually on a predictable schedule)
2) Have everyone wait for 6 months (loosing $ the whole time) while I plead with you to build the bridge for me.

By definition, the least thought through frameworks are the ones most likely to need workarounds, but under this proposal, they are also the ones we will be unable to fix. I think some people think that this proposal will make them fix those frameworks or make them disappear… but they are often from big brand name companies, who don’t care that much because tech isn’t their main business. In my experience, we can get the changes we need, but it takes anywhere from 2 months to a year. Being able to patch in a stop-gap while we are waiting is very important for the health of the business.

For example, I had a recent client that called me in a panic (unfortunately I have somehow gotten a reputation as someone to call for impossible tasks) because they had a demo they needed to show for a potential multimillion dollar deal and it just wasn’t working. The tech they had used as a base wasn’t doing what it was supposed to… and the fixes were 3-6 months away (the demo was a week and a half away). I would call the support guy for the tech, and they would tell me “that isn’t possible yet. Just wait for the update”. I would call them back a couple of hours later saying “If someone else asks, here is how I did it…” Was that code beautiful? No. Did I get all the features in that demo working? Yes, with something like 1 hour to spare. The demo went very very well.

Should I have let that deal fall through because it wasn’t the “proper” ideological way to write code? Sometimes things just need to get done and there isn’t another way…. A few people have suggested that these types of concerns aren’t relevant, but I find them very relevant to my everyday life. This is the first proposal with the ability to actually cost me (and my clients) money.

I am completely ok with needing to type “unsafe” (or similar) to acknowledge and take responsibility for my actions in those situations. I understand those modifications might break when the framework is finally updated in 3-6 months (hopefully we can even remove them at that point). Just don’t “safety" my clients out of business by making working around bad frameworks impossible.

One last analogy. At a restaurant, they might be afraid I would cut myself with a sharp knife. They don’t force me to wear mittens or otherwise make using knifes impossible. They give everyone butterknives, but I can always get a real knife if I ask for one. If I order steak, I don’t even have to ask… they just bring me a real knife. This is how Swift has been and should continue to be IMHO. Prevent me from subclassing accidentally, but if I acknowledge the risk, let me do it. “Safe-by-default” != “Impossible-by-default”

Thanks,
Jon

P.S. This discussion is reminding me of one of my favorite blogs. He often talks about the tension between doing things right, and actually getting out there and doing things. Here is a good/relevant article:
http://prog21.dadgum.com/87.html

···

On Jul 10, 2016, at 7:48 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

On 11 Jul 2016, at 12:33 PM, Jonathan Hull <jhull@gbis.com> wrote:

It is pre-breaking in that it is the exact same code that doesn’t work in both cases (only in the pre-breaking case, a bunch of other code also doesn’t work). I know it feels different because it “was never possible” vs our change being the cause, but it is one of those things like me giving you $5 or giving you $10 and later taking back $5. As humans we are loss averse so we devise strategies to hide the loss from ourselves.

I completely disagree with this.

Not providing someone something due to risk of breakage is not the same thing as “giving it and taking it away”. We don’t build bridges out of spare parts and tell people “We build it but we expect it to break at some stage, so use with caution.” You either build it correctly, or you don’t let people cross the bridge. At All.

This isn’t about “loss averse”. This is about risk management.

Where does the line lie on risk? That’s ultimately something the core team will have to decide.

-
-

With the existence of Swift on the server, dynamically linked, independently distributed frameworks will not be an Apple-only issue - this extends beyond Apple's OS X-based platforms towards how dynamic frameworks link against each other as if they are to be distributed separately.

It is short sighted to suggest that all Swift deployments will be under Apple's control.

I'm really looking forward for server-side Swift — I'm planning for years to extend my portfolio in that direction, and Swift could really push that diversification.

Server side swift is already alive: IBM Developer

···

Le 11 juil. 2016 à 09:39, Tino Heth via swift-evolution <swift-evolution@swift.org> a écrit :

But I had a concrete reason for interest in writing my own backend-code:
Server-side was imho broken on large scale, and it still isn't fixed yet… I can run circles around those poor Java-developers who have to fight crusted structures and deal with sluggish tools like Maven and Tomcat (and Java ;-).*
It seems to me I'm not alone with my opinion, because there are already alternatives on the rise:
Look at Docker — it's a huge success, because it not only takes application and libraries to build a robust unit; it even includes a whole OS!

On iOS, it already hurts when you have a bunch of Swift-Apps which all have the stdlib bundled — but on the server, this doesn't matter, and I'm convinced it would be a bad move to propagate shared frameworks.

- Tino

* of course, there are agile alternatives — but in my environment, most of the big players wouldn't even consider something like Rails

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

Regards
(From mobile)

···

On Jul 11, 2016, at 12:53 PM, Tino Heth <2th@gmx.de> wrote:

You do realize that then itunes store used to (i don't know hese days) for many years run on java, despite objc being such a more advanced environment. ;-)

well, from my experience, app developers are running circles around the itunes store all the time ;-)

Better yet... I once had lunch with the now defunct java team and the guy who did the java rewrite of webobjects... can you guess from what language? or why they had to do it?
Still running circles?
;-)
Cheers & thank u for the smiles...

WebObjects is one of the frameworks I was referring to without actually naming it.

After Apple stopped supporting WebObjects we have been able to fix, extend and enhance it all these years and has served us really well. It is an awesome framework. I really wish that Swift had a framework like it. But Swift's limited reflection is lacking and is crucial for implementing a framework like WebObjects.

Had WebObjects been designed with sealed classes, our hands would have been tied for all the many apps and systems that use it.

I am hoping that if this proposal gets approved that libraries such as these get designed as subclassable. That to me is much more valuable, at least for these types of libraries.

I think that in the long term libraries will sort themselves out. A well designed subclassable library will have more value.

···

On Jul 11, 2016, at 12:49 PM, L. Mihalkovic via swift-evolution <swift-evolution@swift.org> wrote:

Regards
(From mobile)

On Jul 11, 2016, at 12:53 PM, Tino Heth <2th@gmx.de> wrote:

You do realize that then itunes store used to (i don't know hese days) for many years run on java, despite objc being such a more advanced environment. ;-)

well, from my experience, app developers are running circles around the itunes store all the time ;-)

Better yet... I once had lunch with the now defunct java team and the guy who did the java rewrite of webobjects... can you guess from what language? or why they had to do it?
Still running circles?
;-)
Cheers & thank u for the smiles...

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

Thank you for putting your real world scenario out there too. I think you
made some pragmatic points about life of application developers depending
on other companies libraries and I am curious to see more debate on that
point of view.

···

On Tue, Jul 12, 2016 at 12:00 AM, Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

> On Jul 10, 2016, at 7:48 PM, Rod Brown <rodney.brown6@icloud.com> wrote:
>
>> On 11 Jul 2016, at 12:33 PM, Jonathan Hull <jhull@gbis.com> wrote:
>>
>> It is pre-breaking in that it is the exact same code that doesn’t work
in both cases (only in the pre-breaking case, a bunch of other code also
doesn’t work). I know it feels different because it “was never possible”
vs our change being the cause, but it is one of those things like me giving
you $5 or giving you $10 and later taking back $5. As humans we are loss
averse so we devise strategies to hide the loss from ourselves.
>
> I completely disagree with this.
>
> Not providing someone something due to risk of breakage is not the same
thing as “giving it and taking it away”. We don’t build bridges out of
spare parts and tell people “We build it but we expect it to break at some
stage, so use with caution.” You either build it correctly, or you don’t
let people cross the bridge. At All.
>
> This isn’t about “loss averse”. This is about risk management.
>
> Where does the line lie on risk? That’s ultimately something the core
team will have to decide.

My point is that you are completely ignoring an entire class of risk that
has a real-world $$$ cost. Every time I have to use a framework under this
proposal, I am now completely at the mercy of the author. In the case of
open source frameworks I can at least make a fork, but for closed source
frameworks (often from large companies where us using their framework has
been negotiated by the bosses) you have now just added weeks to my
development cycle while I wait for
big-company-who-doesn’t-really-care-that-much to update their stuff. (sure
I can work on other things during that time, but delaying a launch isn’t
free)

Are you offering to explain to my boss/client why I can’t add the feature
in a reasonable timeframe like I can with Objective C frameworks? That it
may not even be possible now in Swift even though the Android guy just did
it in a few hours?

Do you know what I am going to tell my boss/client? "Don’t use Swift for
frameworks” and “Try to avoid partnering with companies that have Swift
frameworks”. "It is too much of a risk". "We are giving them too much
control over the future of our product…" I mean, it even affects the
prices that companies can charge for crappy frameworks. If I am asking for
a bunch of features that I NEED them to add to provide a basic feature,
that affects negotiations/price (vs a world where I can add it myself if
needed). Sealed-by-default gives them leverage.

To use your bridge analogy, which is better in the case that you haven’t
provided a bridge for me:
1) I build my own bridge knowing that I will need to maintain it
periodically (usually on a predictable schedule)
2) Have everyone wait for 6 months (loosing $ the whole time) while I
plead with you to build the bridge for me.

By definition, the least thought through frameworks are the ones most
likely to need workarounds, but under this proposal, they are also the ones
we will be unable to fix. I think some people think that this proposal
will make them fix those frameworks or make them disappear… but they are
often from big brand name companies, who don’t care that much because tech
isn’t their main business. In my experience, we can get the changes we
need, but it takes anywhere from 2 months to a year. Being able to patch
in a stop-gap while we are waiting is very important for the health of the
business.

For example, I had a recent client that called me in a panic
(unfortunately I have somehow gotten a reputation as someone to call for
impossible tasks) because they had a demo they needed to show for a
potential multimillion dollar deal and it just wasn’t working. The tech
they had used as a base wasn’t doing what it was supposed to… and the fixes
were 3-6 months away (the demo was a week and a half away). I would call
the support guy for the tech, and they would tell me “that isn’t possible
yet. Just wait for the update”. I would call them back a couple of hours
later saying “If someone else asks, here is how I did it…” Was that code
beautiful? No. Did I get all the features in that demo working? Yes, with
something like 1 hour to spare. The demo went very very well.

Should I have let that deal fall through because it wasn’t the “proper”
ideological way to write code? Sometimes things just need to get done and
there isn’t another way…. A few people have suggested that these types of
concerns aren’t relevant, but I find them very relevant to my everyday
life. This is the first proposal with the ability to actually cost me (and
my clients) money.

I am completely ok with needing to type “unsafe” (or similar) to
acknowledge and take responsibility for my actions in those situations. I
understand those modifications might break when the framework is finally
updated in 3-6 months (hopefully we can even remove them at that point).
Just don’t “safety" my clients out of business by making working around bad
frameworks impossible.

One last analogy. At a restaurant, they might be afraid I would cut
myself with a sharp knife. They don’t force me to wear mittens or
otherwise make using knifes impossible. They give everyone butterknives,
but I can always get a real knife if I ask for one. If I order steak, I
don’t even have to ask… they just bring me a real knife. This is how Swift
has been and should continue to be IMHO. Prevent me from subclassing
accidentally, but if I acknowledge the risk, let me do it.
“Safe-by-default” != “Impossible-by-default”

Thanks,
Jon

P.S. This discussion is reminding me of one of my favorite blogs. He
often talks about the tension between doing things right, and actually
getting out there and doing things. Here is a good/relevant article:
Write Code Like You Just Learned How to Program

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