Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-17 Thread Jordan Rose via swift-evolution


> On Jan 17, 2018, at 14:42, Jordan Rose  wrote:
> 
> 
> 
>> On Jan 17, 2018, at 14:41, Chris Lattner > > wrote:
>> 
>>> On Jan 16, 2018, at 10:24 AM, Jordan Rose >> > wrote:
> On Jan 12, 2018, at 3:08 PM, Jordan Rose  > wrote:
> 
> Okay, I went back to `unknown case` in the proposal, but mentioned 
> Chris's point very specifically: if the compiler emits an error, we 
> should go with `case #unknown` instead. (I'm very strongly in the 
> "warning" camp, though.)
 
 Thanks!
 
 Out of curiosity, why not “unknown default:”?  The “warning” behavior is a 
 customization of default, so this seems like a more logical model.  It 
 also fits into the existing Swift grammar, unlike “unknown case:” which 
 requires adding a new special case production.
>>> 
>>> I'm not sure how this fits more into the existing grammar. Both of them 
>>> require custom parsing with one token's worth of lookahead. You're right 
>>> that they suggest different natural modelings in the AST, but that's an 
>>> implementation detail.
>> 
>> The parser has fairly general support for declmodifiers, and my proposal 
>> fits directly into that.  The only extension is that ‘default’ isn’t a decl, 
>> and I don’t think we have a statement modifier yet.  That said, we’ve always 
>> planned to have them when the need arose.
> 
> ‘case’ isn’t a decl in switches either. Nor is it a statement, the way things 
> are modeled today.

Bleah, sorry, I forgot that it is a statement at the moment. But I don't think 
it needs to be modeled as a statement attribute/modifier. A flag on CaseStmt is 
fine.

Jordan

> But this is all implementation detail; it’s not interesting to discuss that.
> 
> Jordan

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-17 Thread Jordan Rose via swift-evolution


> On Jan 17, 2018, at 14:41, Chris Lattner  wrote:
> 
>> On Jan 16, 2018, at 10:24 AM, Jordan Rose > > wrote:
 On Jan 12, 2018, at 3:08 PM, Jordan Rose > wrote:
 
 Okay, I went back to `unknown case` in the proposal, but mentioned Chris's 
 point very specifically: if the compiler emits an error, we should go with 
 `case #unknown` instead. (I'm very strongly in the "warning" camp, though.)
>>> 
>>> Thanks!
>>> 
>>> Out of curiosity, why not “unknown default:”?  The “warning” behavior is a 
>>> customization of default, so this seems like a more logical model.  It also 
>>> fits into the existing Swift grammar, unlike “unknown case:” which requires 
>>> adding a new special case production.
>> 
>> I'm not sure how this fits more into the existing grammar. Both of them 
>> require custom parsing with one token's worth of lookahead. You're right 
>> that they suggest different natural modelings in the AST, but that's an 
>> implementation detail.
> 
> The parser has fairly general support for declmodifiers, and my proposal fits 
> directly into that.  The only extension is that ‘default’ isn’t a decl, and I 
> don’t think we have a statement modifier yet.  That said, we’ve always 
> planned to have them when the need arose.

‘case’ isn’t a decl in switches either. Nor is it a statement, the way things 
are modeled today. But this is all implementation detail; it’s not interesting 
to discuss that.

Jordan

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-16 Thread Jordan Rose via swift-evolution


> On Jan 14, 2018, at 09:36, Vladimir.S  wrote:
> 
> 
> 
> On 12.01.2018 21:38, Jordan Rose wrote:
>>> On Jan 12, 2018, at 06:49, Michel Fortin via swift-evolution 
>>> > wrote:
>>> 
 Le 12 janv. 2018 à 4:44, Vladimir.S via swift-evolution 
 > a écrit :
 
 On 12.01.2018 10:30, Chris Lattner via swift-evolution wrote:
>> On Jan 11, 2018, at 11:15 PM, Jean-Daniel via swift-evolution 
>> > wrote:
>> 
>> A question about the new #unknown behavior. Is it intended to be used 
>> for error handling too ?
>> Will it be possible to use in catch clause ?
> If we go with the #unknown approach, then yes of course it will work in 
> catch clauses.  They are patterns, so it naturally falls out.
> If we go with the “unknown default:” / “unknown case:"  approach, then 
> no, this has nothing to do with error handling.
> IMO, this pivots on the desired semantics for “unknown cases in enums”: 
> if you intentionally try to match on this, do we get a warning or error 
> if you don’t handle all the cases?  If we can get to consensus on that 
> point, then the design is pretty obvious IMO.
 
 For me the other question is what "all the cases" means for enum with 
 private cases(if we'll have them). I.e. if switch contains all the 
 "public" cases of frozen enum - does this mean "all the cases" were 
 processed? As I understand, the answer is no, because we *can* have 
 'private' case value here and so we need to react to this. How switch will 
 look in this case?
 
 switch frozenEnumWithPrivateCases {
  case .one: ..
  case .two: ..
  unknown default: ..  // or 'case #unknown:' depending on our decision, or 
 'unknown case:' etc
 }
 ?
 But then such switch looks exactly as switch for non-frozen enum value, 
 no? It looks like we are reacting on future new cases, while enum is 
 frozen.
 
 Moreover. How the switch for non-frozed enum with private cases should 
 looks like?
 
 switch nonfrozenEnumWithPrivateCases {
  case .one: ..
  case .two: ..
  unknown default: ..  // or 'case #unknown:' depending on our decision, or 
 'unknown case:' etc
 }
 ? But then, is that 'unknown default' for reacting on "future" cases we 
 didn't know about during the compilation OR it is for reacting on private 
 cases?
 
 Or the main idea that we don't want to separate "future" cases and 
 "private" cases?
>>> 
>>> I think treating both as the same thing is the right idea. You also need to 
>>> handle "future private" cases and "private cases that become public in the 
>>> future". These are all unknown cases in the context of the switch.
>>> 
>>> So an enum with private cases can't be switched exhaustively outside of its 
>>> module. Thus, @frozen would need to forbid private cases... or we need 
>>> @exhaustive to forbid private cases so they can be allowed by @frozen.
>> As mentioned in "Future directions", my recommendation to anyone planning to 
>> write a proposal for non-public cases is to go with the former, which would 
>> keep it from infecting the design.
> 
> Thank you for the comment!
> From proposal:
> "Were such a proposal to be written, I advise that a frozen enum not be 
> permitted to have non-public cases."
> 
> OK. Seems logically for frozen enum(imported from another module) to not have 
> non-public cases, as such cases most likely will be added later during the 
> evaluation of the library(external module) - so such enum should not be 
> frozen then.
> 
> I'd like to discuss how current decision will fit into the (possible) future 
> 'private cases' in enum.
> 
> 1. Non-frozen enum with private cases in the same module.
> 
> It seems like we'll need to write
> switch val {
> case .one : ..
> case .two : ..
> unknown default: .. // for private cases, even 'val' can't have 'future' cases
> }
> 
> So, 'unknown default' will mean not just 'future cases', but 'future cases 
> and private cases'.

Interesting. I didn't think about that; in the original proposal without any 
warnings, you just had to use 'default'. But it does make sense, and whoever 
proposes private cases will have to mention that.



> 
> 2. Non-frozen enum with private cases in another module.
> 
> In this case, if we want exhaustive switch, we need to use 'unknown default'. 
> But I feel like known but private cases are not the same as future public 
> cases for the 'consumer' of that enum, no?
> 
> I mean, when making a decision what to do inside 'unknown default' - isn't it 
> important to know what is the "event" - new public case or private(even 
> "known") case? I'm thinking about something like this:
> 
> let val = 

Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-16 Thread Jordan Rose via swift-evolution


> On Jan 13, 2018, at 18:33, Chris Lattner  wrote:
> 
> I don’t understand why #unknown wouldn’t work in catch clauses.  In the 
> absence of typed throws you can’t match on an enums case without the enums 
> base:  you can’t use .foo, you have to use MyEnum.foo.
> 
> Similarly, catch wouldn’t allow .#unknown, it would require MyEnum.#unknown.  
> This is perfectly well defined and just falls out of the model.

I did not think about this. You're right, we could allow that syntax. We don't 
currently have any hash-prefixed constructs that aren't "top-level", but that's 
not a rule or anything.

Jordan

> 
> That said, I agree that the issue of source dependencies that might use this 
> is a significant problem.  IMO, that argues strongly for “unknown default:” 
> producing a warning.
> 
> -Chris
> 
>> On Jan 12, 2018, at 10:49 PM, Chris Lattner via swift-evolution 
>>  wrote:
>> 
>> 
>>> On Jan 12, 2018, at 3:08 PM, Jordan Rose  wrote:
>>> 
>>> Okay, I went back to `unknown case` in the proposal, but mentioned Chris's 
>>> point very specifically: if the compiler emits an error, we should go with 
>>> `case #unknown` instead. (I'm very strongly in the "warning" camp, though.)
>> 
>> Thanks!
>> 
>> Out of curiosity, why not “unknown default:”?  The “warning” behavior is a 
>> customization of default, so this seems like a more logical model.  It also 
>> fits into the existing Swift grammar, unlike “unknown case:” which requires 
>> adding a new special case production.
>> 
>> -Chris
>> 
>> ___
>> 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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-16 Thread Jordan Rose via swift-evolution
Is this equivalent to `default: break`? `default: fatalError()`? I think it's 
better to write such things explicitly.

Jordan


> On Jan 14, 2018, at 21:51, Tim Wang  wrote:
> 
> I would like to add a syntax sugar .casesBelow for enum value to be used in 
> switch sentence to avoid default case.
> 
> enum MyEnum {
> case a
> case b
> case c
> }
> 
> let myEnum: MyEnum  = .a
> 
> //Normally we need default case
> switch myEnum {
> case .a: print("a")
> default: print("other value")
> }
> 
> //Now use syntax sugar
> switch myEnum.casesBelow {
> case .a: print("a")
> }
> 
> 
> This would look more intuitive to me than other solutions but I am not sure 
> how much effort we need for this.
> 
> 
> 
> On Mon, Jan 15, 2018 at 4:36 AM Vladimir.S via swift-evolution 
> > wrote:
> 
> 
> On 12.01.2018 21:38, Jordan Rose wrote:
> >
> >
> >> On Jan 12, 2018, at 06:49, Michel Fortin via swift-evolution 
> >> 
> >> >> 
> >> wrote:
> >>
> >>> Le 12 janv. 2018 à 4:44, Vladimir.S via swift-evolution 
> >>> 
> >>> >> a 
> >>> écrit :
> >>>
> >>> On 12.01.2018 10:30, Chris Lattner via swift-evolution wrote:
> > On Jan 11, 2018, at 11:15 PM, Jean-Daniel via swift-evolution 
> > 
> > >> 
> > wrote:
> >
> > A question about the new #unknown behavior. Is it intended to be used 
> > for error handling too ?
> > Will it be possible to use in catch clause ?
>  If we go with the #unknown approach, then yes of course it will work in 
>  catch clauses.  They are patterns, so it
>  naturally falls out.
>  If we go with the “unknown default:” / “unknown case:"  approach, then 
>  no, this has nothing to do with error handling.
>  IMO, this pivots on the desired semantics for “unknown cases in enums”: 
>  if you intentionally try to match on this,
>  do we get a warning or error if you don’t handle all the cases?  If we 
>  can get to consensus on that point, then the
>  design is pretty obvious IMO.
> >>>
> >>> For me the other question is what "all the cases" means for enum with 
> >>> private cases(if we'll have them). I.e. if
> >>> switch contains all the "public" cases of frozen enum - does this mean 
> >>> "all the cases" were processed? As I
> >>> understand, the answer is no, because we *can* have 'private' case value 
> >>> here and so we need to react to this. How
> >>> switch will look in this case?
> >>>
> >>> switch frozenEnumWithPrivateCases {
> >>>  case .one: ..
> >>>  case .two: ..
> >>>  unknown default: ..  // or 'case #unknown:' depending on our decision, 
> >>> or 'unknown case:' etc
> >>> }
> >>> ?
> >>> But then such switch looks exactly as switch for non-frozen enum value, 
> >>> no? It looks like we are reacting on future
> >>> new cases, while enum is frozen.
> >>>
> >>> Moreover. How the switch for non-frozed enum with private cases should 
> >>> looks like?
> >>>
> >>> switch nonfrozenEnumWithPrivateCases {
> >>>  case .one: ..
> >>>  case .two: ..
> >>>  unknown default: ..  // or 'case #unknown:' depending on our decision, 
> >>> or 'unknown case:' etc
> >>> }
> >>> ? But then, is that 'unknown default' for reacting on "future" cases we 
> >>> didn't know about during the compilation OR
> >>> it is for reacting on private cases?
> >>>
> >>> Or the main idea that we don't want to separate "future" cases and 
> >>> "private" cases?
> >>
> >> I think treating both as the same thing is the right idea. You also need 
> >> to handle "future private" cases and "private
> >> cases that become public in the future". These are all unknown cases in 
> >> the context of the switch.
> >>
> >> So an enum with private cases can't be switched exhaustively outside of 
> >> its module. Thus, @frozen would need to forbid
> >> private cases... or we need @exhaustive to forbid private cases so they 
> >> can be allowed by @frozen.
> >
> > As mentioned in "Future directions", my recommendation to anyone planning 
> > to write a proposal for non-public cases is to
> > go with the former, which would keep it from infecting the design.
> >
> 
> Thank you for the comment!
>  From proposal:
> "Were such a proposal to be written, I advise that a frozen enum not be 
> permitted to have non-public cases."
> 
> OK. Seems logically for frozen enum(imported from another module) to not have 
> non-public cases, as such cases most
> likely will be added later during the evaluation of the library(external 
> module) - so such enum should not be frozen then.
> 
> I'd like to 

Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-12 Thread Jordan Rose via swift-evolution
No, it's "Revision-locked imports".

A source-breaking change is one in which updating the library at compile time 
will break clients at compile time. That's relevant for libraries distributed 
with an app as well as for libraries that are part of the OS. You may not care, 
but I do, and I think other package authors will too.

Jordan


> On Jan 12, 2018, at 15:20, Dave DeLong <sw...@davedelong.com> wrote:
> 
> I assume you’re referring to this?
> Implicitly treat enums without binary compatibility concerns as @frozen
> 
> Several people questioned whether it was necessary to make this distinction 
> for libraries without binary compatibility concerns, i.e. those that are 
> shipped with their client apps. While there may be a need to do something to 
> handle enums shipped with Apple's OS SDKs, it's arguable whether this is 
> worth it for "source libraries", such as SwiftPM packages.
> 
> This question can be rephrased as "is adding a case to an enum a 
> source-breaking change?" 
> 
> 
> I don’t follow on how this is a source-breaking change.
> 
> Let’s say I’m writing an app “A” that embeds a module “M”, which defines and 
> enum “E”.
> 
> If E is frozen, the everything’s fine and all is well.
> If E is *not* frozen, then the compiler is going to force me to put in an 
> “unknown case”, because it has falsely concluded that E may change out from 
> underneath my app.
> 
> The will be a mistake on the compiler’s part. I do not need to include the 
> “unknown case” handler while enumerating on E, because there is no 
> possibility of ever getting an unknown enum value from M, because it is 
> statically copied in to my app and will never change.
> 
> Dave
> 
>> On Jan 12, 2018, at 4:16 PM, Jordan Rose <jordan_r...@apple.com 
>> <mailto:jordan_r...@apple.com>> wrote:
>> 
>> I included that, but also I again don't think that's the correct design. 
>> There's a case where that's useful, but that shouldn't be the default, and I 
>> don't think it's important enough to do in Swift 5.
>> 
>> Jordan
>> 
>> 
>>> On Jan 12, 2018, at 15:15, Dave DeLong <sw...@davedelong.com 
>>> <mailto:sw...@davedelong.com>> wrote:
>>> 
>>> Unless I’m missing something, this is still missing the discussion on being 
>>> able to treat all enums of internally-packaged libraries as frozen.
>>> 
>>> IE, that frozen vs unfrozen is only an issue for enums that come from 
>>> modules that are not packaged with your app.
>>> 
>>> Dave
>>> 
>>>> On Jan 12, 2018, at 4:08 PM, Jordan Rose via swift-evolution 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>> 
>>>> Okay, I went back to `unknown case` in the proposal, but mentioned Chris's 
>>>> point very specifically: if the compiler emits an error, we should go with 
>>>> `case #unknown` instead. (I'm very strongly in the "warning" camp, though.)
>>>> 
>>>> I think the revised proposal is in good shape! 
>>>> (https://github.com/apple/swift-evolution/pull/777 
>>>> <https://github.com/apple/swift-evolution/pull/777>) I think I've 
>>>> addressed everyone's feedback either in the proposal or on-list, if not 
>>>> necessarily convinced them. If there are no other major comments I'll let 
>>>> Ted know that it's ready to re-run on Monday.
>>>> 
>>>> Jordan
>>>> ___
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>> 
> 

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-12 Thread Jordan Rose via swift-evolution
I included that, but also I again don't think that's the correct design. 
There's a case where that's useful, but that shouldn't be the default, and I 
don't think it's important enough to do in Swift 5.

Jordan


> On Jan 12, 2018, at 15:15, Dave DeLong <sw...@davedelong.com> wrote:
> 
> Unless I’m missing something, this is still missing the discussion on being 
> able to treat all enums of internally-packaged libraries as frozen.
> 
> IE, that frozen vs unfrozen is only an issue for enums that come from modules 
> that are not packaged with your app.
> 
> Dave
> 
>> On Jan 12, 2018, at 4:08 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org> wrote:
>> 
>> Okay, I went back to `unknown case` in the proposal, but mentioned Chris's 
>> point very specifically: if the compiler emits an error, we should go with 
>> `case #unknown` instead. (I'm very strongly in the "warning" camp, though.)
>> 
>> I think the revised proposal is in good shape! 
>> (https://github.com/apple/swift-evolution/pull/777) I think I've addressed 
>> everyone's feedback either in the proposal or on-list, if not necessarily 
>> convinced them. If there are no other major comments I'll let Ted know that 
>> it's ready to re-run on Monday.
>> 
>> Jordan
>> ___
>> 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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-12 Thread Jordan Rose via swift-evolution
Okay, I went back to `unknown case` in the proposal, but mentioned Chris's 
point very specifically: if the compiler emits an error, we should go with 
`case #unknown` instead. (I'm very strongly in the "warning" camp, though.)

I think the revised proposal is in good shape! 
(https://github.com/apple/swift-evolution/pull/777) I think I've addressed 
everyone's feedback either in the proposal or on-list, if not necessarily 
convinced them. If there are no other major comments I'll let Ted know that 
it's ready to re-run on Monday.

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-12 Thread Jordan Rose via swift-evolution


> On Jan 12, 2018, at 06:49, Michel Fortin via swift-evolution 
>  wrote:
> 
>> Le 12 janv. 2018 à 4:44, Vladimir.S via swift-evolution 
>> > a écrit :
>> 
>> On 12.01.2018 10:30, Chris Lattner via swift-evolution wrote:
 On Jan 11, 2018, at 11:15 PM, Jean-Daniel via swift-evolution 
 > wrote:
 
 A question about the new #unknown behavior. Is it intended to be used for 
 error handling too ?
 Will it be possible to use in catch clause ?
>>> If we go with the #unknown approach, then yes of course it will work in 
>>> catch clauses.  They are patterns, so it naturally falls out.
>>> If we go with the “unknown default:” / “unknown case:"  approach, then no, 
>>> this has nothing to do with error handling.
>>> IMO, this pivots on the desired semantics for “unknown cases in enums”: if 
>>> you intentionally try to match on this, do we get a warning or error if you 
>>> don’t handle all the cases?  If we can get to consensus on that point, then 
>>> the design is pretty obvious IMO.
>> 
>> For me the other question is what "all the cases" means for enum with 
>> private cases(if we'll have them). I.e. if switch contains all the "public" 
>> cases of frozen enum - does this mean "all the cases" were processed? As I 
>> understand, the answer is no, because we *can* have 'private' case value 
>> here and so we need to react to this. How switch will look in this case?
>> 
>> switch frozenEnumWithPrivateCases {
>>  case .one: ..
>>  case .two: ..
>>  unknown default: ..  // or 'case #unknown:' depending on our decision, or 
>> 'unknown case:' etc
>> }
>> ?
>> But then such switch looks exactly as switch for non-frozen enum value, no? 
>> It looks like we are reacting on future new cases, while enum is frozen.
>> 
>> Moreover. How the switch for non-frozed enum with private cases should looks 
>> like?
>> 
>> switch nonfrozenEnumWithPrivateCases {
>>  case .one: ..
>>  case .two: ..
>>  unknown default: ..  // or 'case #unknown:' depending on our decision, or 
>> 'unknown case:' etc
>> }
>> ? But then, is that 'unknown default' for reacting on "future" cases we 
>> didn't know about during the compilation OR it is for reacting on private 
>> cases?
>> 
>> Or the main idea that we don't want to separate "future" cases and "private" 
>> cases?
> 
> I think treating both as the same thing is the right idea. You also need to 
> handle "future private" cases and "private cases that become public in the 
> future". These are all unknown cases in the context of the switch.
> 
> So an enum with private cases can't be switched exhaustively outside of its 
> module. Thus, @frozen would need to forbid private cases... or we need 
> @exhaustive to forbid private cases so they can be allowed by @frozen.

As mentioned in "Future directions", my recommendation to anyone planning to 
write a proposal for non-public cases is to go with the former, which would 
keep it from infecting the design.

Jordan

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-12 Thread Jordan Rose via swift-evolution


> On Jan 11, 2018, at 23:30, Chris Lattner  wrote:
> 
> 
>> On Jan 11, 2018, at 11:15 PM, Jean-Daniel via swift-evolution 
>>  wrote:
>> 
>> A question about the new #unknown behavior. Is it intended to be used for 
>> error handling too ?
>> Will it be possible to use in catch clause ?
> 
> If we go with the #unknown approach, then yes of course it will work in catch 
> clauses.  They are patterns, so it naturally falls out.

It will not work in catch clauses because you need to have a static type that's 
an enum. Catch clauses always (today…) have a static type of 'Error'.


> 
> If we go with the “unknown default:” / “unknown case:"  approach, then no, 
> this has nothing to do with error handling.
> 
> IMO, this pivots on the desired semantics for “unknown cases in enums”: if 
> you intentionally try to match on this, do we get a warning or error if you 
> don’t handle all the cases?  If we can get to consensus on that point, then 
> the design is pretty obvious IMO.

That's fair. I'm strongly in favor of a warning, though, because again, people 
don't edit their dependencies.

Jordan

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-11 Thread Jordan Rose via swift-evolution


> On Jan 11, 2018, at 05:08, Michel Fortin  wrote:
> 
> I think `unknown` should be a modifier for either `case` or `default`. This 
> would allow:
> 
>   unknown default:
>   unknown case _: // similar to default
>   unknown case (1, _): // enum in second position
> 
> If the case can be reached with statically known enum values, the compiler 
> generates a warning.
> 
> I'd also prefer a more precise term instead of "unknown". What we aim at is 
> matching cases that do not have a declaration (future cases, 
> privately-declared cases). So I'd use the word "undeclared" rather than 
> "unknown":
> 
>   undeclared default:
>   undeclared case _: // similar to default
>   undeclared case (1, _): // enum in second position
> 
> That word has the advantage that enums are also less likely to have a case 
> named "undeclared", I think.

I’m not sure I’d agree that most people would think of private cases are 
“undeclared”, but sure, it’s a reasonable alternative. I still like “unknown” a 
little better myself.

Jordan


> 
>> Le 10 janv. 2018 à 23:31, Chris Lattner via swift-evolution 
>> > a écrit :
>> 
>> 
>>> On Jan 10, 2018, at 10:10 AM, Jordan Rose >> > wrote:
>>> 
> 
> - Matching known cases is a feature, not a limitation, to avoid existing 
> code changing meaning when you recompile. I'll admit that's not the 
> strongest motivation, though, since other things can change the meaning 
> of existing code when you recompile already.
 
 I’m not sure I understand this. 
 
 The whole motivation for this feature is to notify people if they are not 
 handling a “newly known” case.  If they don’t care about this, they can 
 just use default.
>>> 
>>> Notify, yes. Error, no. It's a design goal that adding a new case does not 
>>> break source compatibility in addition to not breaking binary compatibility 
>>> (because people don't like editing their dependencies) and therefore the 
>>> behavior has to be defined when they recompile with no changes.
>>> 
>> 
>> Ok, if that’s the desired design, then (IMO) the right way to spell it is 
>> “unknown default:” and it should have semantics basically aligned with the 
>> design you laid out in the revision of the proposal.  If this is supposed to 
>> be an error, then it should be a pattern production.
>> 
>> Do you have a sense for whether this is what people want?  We really should 
>> have a review cycle evaluating exactly this sort of tradeoff.
>> 
>> In any case, I’ve said this before off-list, but I find this whole 
>> discussion (of how to improve diagnostics for unknown cases) to be separable 
>> from the core issue required to get to ABI stability.  It seems to me that 
>> we could split this (ongoing) design discussion off into a separate SE, 
>> allowing you to get on with the relatively uncontroversial and critical 
>> parts in SE-0192.
>> 
>> -Chris
>> 
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org 
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> 
> 
> -- 
> Michel Fortin
> https://michelf.ca 

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-11 Thread Jordan Rose via swift-evolution
Removing cases just isn’t allowed. They can be deprecated, but actually 
removing one is a breaking change. With a Swift enum the app will actually fail 
to launch with a dynamic linking error.

Jordan


> On Jan 10, 2018, at 21:27, Howard Lovatt  wrote:
> 
> It is a two way street though. An app compiled against an old framework might 
> pass a deleted enum case back to the new framework that has been changed 
> under it. 
> 
> Just as the app has to guard against new cases the framework has to guard 
> against old cases!
> 
> Both ends need an unknown case. 
> 
> -- Howard. 
> 
>> On 10 Jan 2018, at 5:40 pm, Jordan Rose  wrote:
>> 
>> Remember, the goal here is to support both binary and source compatibility. 
>> An existing app might be using the enum case that you're trying to remove, 
>> but there's no chance that an existing app is using an enum case that you're 
>> trying to add.
>> 
>> Jordan
>> 
>> 
>>> On Jan 10, 2018, at 16:34, Howard Lovatt via swift-evolution 
>>>  wrote:
>>> 
>>> If an enum isn’t final; then what’s the difference in deleting as opposed 
>>> to adding?
>>> 
>>> -- Howard. 
>>> 
 On 10 Jan 2018, at 4:13 pm, Jean-Daniel  wrote:
 
 
 
> Le 10 janv. 2018 à 23:58, Howard Lovatt via swift-evolution 
>  a écrit :
> 
> Two points:
> 
> 1. I like Chris’s suggestion of #unknown and in particular that it is 
> distinct from default. 
> 
> 2. All the discussion is about a framework adding a case, what about when 
> a framework deletes a case?
 
 This is a binary breaking change (just like removing an existing function 
 or method).
 
 
>>> ___
>>> 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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-10 Thread Jordan Rose via swift-evolution
Remember, the goal here is to support both binary and source compatibility. An 
existing app might be using the enum case that you're trying to remove, but 
there's no chance that an existing app is using an enum case that you're trying 
to add.

Jordan


> On Jan 10, 2018, at 16:34, Howard Lovatt via swift-evolution 
>  wrote:
> 
> If an enum isn’t final; then what’s the difference in deleting as opposed to 
> adding?
> 
> -- Howard. 
> 
>> On 10 Jan 2018, at 4:13 pm, Jean-Daniel  wrote:
>> 
>> 
>> 
>>> Le 10 janv. 2018 à 23:58, Howard Lovatt via swift-evolution 
>>>  a écrit :
>>> 
>>> Two points:
>>> 
>>> 1. I like Chris’s suggestion of #unknown and in particular that it is 
>>> distinct from default. 
>>> 
>>> 2. All the discussion is about a framework adding a case, what about when a 
>>> framework deletes a case?
>> 
>> This is a binary breaking change (just like removing an existing function or 
>> method).
>> 
>> 
> ___
> 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


Re: [swift-evolution] [Review] SE-0194: Derived Collection of Enum Cases

2018-01-10 Thread Jordan Rose via swift-evolution
[Proposal: 
https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md]

I think this is generally reasonable, and none of the names offend me enough to 
weigh in on that discussion. I do think it's a little weird that @objc enums 
defined in Swift cannot conform to ValueEnumerable, just because imported enums 
won't. (But especially while knee-deep in SE-0192, I think it's correct that 
imported enums won't. The exception could be C enums marked 
`enum_extensibility(closed)`, but I'm not convinced we need that yet.)

The biggest problem I have is unavailable cases. An unavailable case must not 
be instantiated—consider an enum where some cases are only available on iOS and 
not macOS. (I bet we optimize based on this, which makes it all the more 
important to get right.)

I think you should explicitly call out that the derived implementation only 
kicks in when ValueEnumerable is declared on the enum itself, not an extension. 
Or if that's not the case, it should be limited to extensions in the same 
module as the enum. (You could add "unless the enum is '@frozen'", but that's 
not really necessary.)

I don't think this should be implemented with a run-time function; compile-time 
code generation makes more sense to me. But that's an implementation detail; it 
doesn't change the language surface.

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-10 Thread Jordan Rose via swift-evolution


> On Jan 10, 2018, at 12:04, Jose Cheyo Jimenez via swift-evolution 
>  wrote:
> 
> 
> 
> On Tue, Jan 9, 2018 at 10:10 PM, Chris Lattner  > wrote:
> On Jan 9, 2018, at 3:07 PM, Jose Cheyo Jimenez  > wrote:
> > Hi Chris,
> >
> > This is great. Thanks for spending time on this! I am in favor of `case 
> > #unknown` to only match unknown cases.
> >
> > 1) Would #uknown be available to RawRepresentable structs?
> 
> I haven’t considered this, so I’m not sure how that would work.  I had 
> assumed that this would be an enum specific concept.
> 
> What do you have in mind?  If your raw representation is itself an enum, then 
> of course this would work.
> 
> I was just curious since a RawRepresentable structs ( and OptionSet ) are 
> very similar to non exhaustive enums in that they would require handling 
> unknown cases. Currently RawRepresentable is required to use a default even 
> if all cases are accounted for. 
> 
> struct Directions: OptionSet {
> let rawValue: UInt8
> static let up= Directions(rawValue: 1 << 0)
> }
> 
> let myDir = Directions.up
> 
> switch myDir
> {
> case .up : print("matched")
> default: print("default required") 
> }

Since an option set has 2^N possible values, not just N, we don't expect anyone 
to be switching over them! And it wouldn't be correct to try to do so 
exhaustively anyway.

Jordan

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-10 Thread Jordan Rose via swift-evolution

>> That said, it sounds like people are happier with `case #unknown` than 
>> `unknown case`, and that leaves things a little more consistent if we ever 
>> do expand it to other pattern positions, so I'll change the proposal 
>> revision to use that spelling. (And if anyone comes up with a nicer 
>> spelling, great!)
> 
> Thanks!

Updated! https://github.com/apple/swift-evolution/pull/777. Also tried to 
clarify some of the points on why I'm leery about #unknown as an arbitrary 
pattern, at least for now.

Jordan

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-10 Thread Jordan Rose via swift-evolution


> On Jan 9, 2018, at 22:17, Chris Lattner  wrote:
> 
> On Jan 9, 2018, at 4:46 PM, Jordan Rose  > wrote:
>> Thanks for writing this up, Chris! I addressed the idea of making this an 
>> arbitrary pattern in the revised proposal 
>> ,
>>  and the idea of not matching known cases in a "considered alternative" 
>> ,
>>  but in short:
>> 
>> - Matching in arbitrary pattern positions is harder to implement and harder 
>> to produce good diagnostics for. (Specifically, the former comes from having 
>> to actually check the existing cases in the generated code rather than just 
>> having an "else" branch.) I'm not inherently opposed to it if we can all 
>> agree on what it means, but I don't think I have time to implement it for 
>> Swift 5, so I'm not including it in the proposal.
> 
> I’m not sure what you’re saying here.  This does slot directly into the 
> existing pattern design: it is just a new terminal pattern node.  Are you 
> saying the SILGen/IRGen is more difficult and therefore you’re not interested 
> in doing it even if it is the better design?  
> 
> If that is the case, then the conservatively correct thing to do (in my 
> opinion) is to add no feature here: no “unknown case:” and no “case 
> #unknown:’.
> 
> Alternatively are you saying that you intend to support only the “case 
> #unknown:” form exactly, but not the recursive cases?
> 
>> - Matching known cases is a feature, not a limitation, to avoid existing 
>> code changing meaning when you recompile. I'll admit that's not the 
>> strongest motivation, though, since other things can change the meaning of 
>> existing code when you recompile already.
> 
> I’m not sure I understand this. 
> 
> The whole motivation for this feature is to notify people if they are not 
> handling a “newly known” case.  If they don’t care about this, they can just 
> use default.

Notify, yes. Error, no. It's a design goal that adding a new case does not 
break source compatibility in addition to not breaking binary compatibility 
(because people don't like editing their dependencies) and therefore the 
behavior has to be defined when they recompile with no changes.

(This should also cover why it's more difficult to implement both diagnostics 
and SILGen for it.)

Jordan


> 
>> - FWIW I can't actually think of a use case for using this with `if case` or 
>> anything else. I'm not against it, but I don't know why you would ever do 
>> it, just like I don't know why you would mix `case #unknown` with `default` 
>> when matching against a single value.
> 
> Brent gave a simple example down thread.  That said, I’m more concerned about 
> keeping pattern matching simple and composable than I am about particular use 
> cases (I agree the top level case in a switch on enum is the 
> disproportionately important case). 
> 
> At some point we will hopefully extend pattern matching to support regexes 
> and other things, we want to keep the design consistent.
> 
>> That said, it sounds like people are happier with `case #unknown` than 
>> `unknown case`, and that leaves things a little more consistent if we ever 
>> do expand it to other pattern positions, so I'll change the proposal 
>> revision to use that spelling. (And if anyone comes up with a nicer 
>> spelling, great!)
> 
> Thanks!
> 
> -Chris
> 
> 

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-10 Thread Jordan Rose via swift-evolution


> On Jan 9, 2018, at 21:39, Brent Royal-Gordon <br...@architechies.com> wrote:
> 
> 
> 
>> On Jan 9, 2018, at 4:46 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> - FWIW I can't actually think of a use case for using this with `if case` or 
>> anything else. I'm not against it, but I don't know why you would ever do 
>> it, just like I don't know why you would mix `case #unknown` with `default` 
>> when matching against a single value.
> 
>   if case #unknown = someEnum {
>   throw MyError.unknownValue
>   }
>   …

Yes, you can write that, but why would you do it? What are you going to do 
below now that you know 'someEnum' is a known value but the compiler doesn't?

(Also, it doesn't play well with recompiling…)


> 
>> That said, it sounds like people are happier with `case #unknown` than 
>> `unknown case`, and that leaves things a little more consistent if we ever 
>> do expand it to other pattern positions, so I'll change the proposal 
>> revision to use that spelling. (And if anyone comes up with a nicer 
>> spelling, great!)
> 
> I don't love the spelling of `#unknown`—particularly since some enums in the 
> Apple frameworks actually *have* an ordinary case called "unknown"—but I 
> think it's a nice, pragmatic solution which slots into pattern matching very 
> nicely.
> 
> (And if we ever *do* decide to support @testable enum parameters, we'll 
> already have the syntax to specify unknown values.)
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 

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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-09 Thread Jordan Rose via swift-evolution
I'm not sure how this solves the problem. We need to know whether an enum may 
grow new cases or not, a concept that doesn't exist in Swift today. This is 
most interesting for enums in "libraries with binary compatibility concerns", 
but is also interesting for libraries that don't have such concerns, like 
source packages. Source packages don't use availability annotations.

We definitely need to be able to mark when enum cases were introduced, but the 
language already supports that, at least for libraries that are shipped with an 
Apple OS.

As for the name, I agree that 'exhaustive' isn't great. The best suggestion we 
got from the initial review was 'frozen', and that's what I'm putting in the 
next revision of the proposal.

Jordan

P.S. I'm not sure where you got "versioned" from. The attribute currently 
spelled '_versioned' in the Swift compiler is equivalent to what SE-0193 

 calls 'abiPublic', at least in this revision. (I'm sorry to say I haven't 
caught up on all the discussion there, so I don't know if that's going to be 
spelled some other way.)



> On Jan 9, 2018, at 08:52, Jon Gilbert  wrote:
> 
> Having reviewed much of the commentary on this proposal, I keep coming back 
> to the same thought: why not use @versioned and @available keywords for this 
> instead of some concept related to “exhaustive”?
> 
> The issue here is not whether a given enum is “exhaustive” over the 
> enumerated problem space; it’s whether the developer wants to alter the enum 
> in the future without breaking ABI compatibility.
> 
> If you call it “exhaustive” then it’s misleading, because all enums at a 
> given moment in time can be switched over exhaustively. This will just 
> confuse folks.
> 
> Since versioning is really the main goal, why not use the same annotations 
> for versioning enums as are used for versioning everything else?
> 
> @versioned
> enum MyError: Error {
> @available(OSX, deprecated:10.11, message: "this error case is going away in 
> 10.12")
> case BadThingHappened
> 
> @available(forever)
> case ReallyBadThingHappened
> }
> 
> etc.
> 
> That way you could have some cases get removed in the future as well as 
> added, and you won’t confuse people by talking about “complete” or 
> “exhaustive”, which are terms that are too closely coupled with the meaning 
> and application of a given enum to which they refer. 
> 
> It would be best to use terms that just say what they mean. We are talking 
> about versioning APIs to keep ABI stability? Use @versioned and @available 
> like everywhere else. 
> 
> Or is there a compelling reason this cannot be done? I read much of the 
> arguments here but didn’t see any mention of @versioned... maybe it’s iOS 
> Mail app thinking I was looking for an email address that contains 
> @versioned? ;D
> 
> Jonathan

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


Re: [swift-evolution] Handling unknown cases in enums [RE: SE-0192]

2018-01-09 Thread Jordan Rose via swift-evolution


> On Jan 8, 2018, at 22:54, Chris Lattner via swift-evolution 
>  wrote:
> 
> The mega-thread about SE-0192 is a bit large, and I’d like to talk about one 
> specific point.  In the review conversation, there has been significant 
> objection to the idea of requiring a ‘default’ for switches over enums that 
> are non-exhaustive.  
> 
> This whole discussion is happening because the ABI stability work is 
> introducing a new concept to enums - invisible members/inhabitants (and 
> making them reasonably prominent).  A closely related feature is that may 
> come up in the future is "private cases”.  Private cases are orthogonal to 
> API evolution and may even occur on one that is defined to be exhaustive.
> 
> Private cases and non-exhaustive enums affect the enum in the same way: they 
> say that the enum can have values that a client does not know about.  Swift 
> requires switches to process *all* of the dynamically possible values, which 
> is why the original proposal started out with the simplest possible solution: 
> just require ‘default' when processing the cases.
> 
> 
> The problems with “unknown case:”
> 
> The popular solution to this probably is currently being pitched as a change 
> to the proposal (https://github.com/apple/swift-evolution/pull/777 
> ) which introduces a new 
> concept “unknown case” as a near-alias for ‘default’:
> https://github.com/jrose-apple/swift-evolution/blob/60d8698d7cde2e1824789b952558bade541415f1/proposals/0192-non-exhaustive-enums.md#unknown-case
>  
> 
> 
> In short, I think this is the wrong way to solve the problem.  I have several 
> concerns with this:
> 
> 1) Unlike in C, switch is Swift is a general pattern matching facility - not 
> a way of processing integers.  It supports recursive patterns and values, and 
> enums are not necessarily at the top-level of the pattern.   
> https://github.com/apple/swift/blob/master/docs/PatternMatching.rst is a 
> document from early evolution of Swift but contains a good general 
> introduction to this.
> 
> 2) Swift also has other facilities for pattern matching, including ‘if case’. 
>  Making switch inconsistent with them is not great.
> 
> 3) As pitched, “unknown case” will match *known* cases too, which is (in my 
> opinion :-) oxymoronic.
> 
> 4) “unknown case:” changes the basic swift grammar (it isn’t just a modifier 
> on case) because case *requires* a pattern.  A better spelling would be 
> “unknown default:” which is closer to the semantic provided anyway.
> 
> 5) It is entirely reasonable (though rare in practice) to want to handle 
> default and unknown cases in the same switch.
> 
> 
> For all the above reasons, ‘unknown case:' becomes a weird wart put on the 
> side of switch/case, not something that fits in naturally with the rest of 
> Swift.
> 
> 
> Alternative proposal:
> 
> Instead of introducing a new modifier on case/switch, lets just introduce a 
> new pattern matching operator that *matches unknown cases*, called “#unknown” 
> or “.#unknown” or something (I’m not wed to the syntax, better suggestions 
> welcome :).
> 
> In the simple case most people are talking about, instead of writing “unknown 
> case:” you’d write “case #unknown:” which isn’t much different.  The nice 
> thing about this is that #unknown slots directly into our pattern matching 
> system.  Here is a weird example:
> 
> switch someIntEnumTuple {
> case (1, .X):   … matches one combination of int and tuple...
> case (2, .Y):   … matches another combination of int and tuple...
> case (_, #unknown): …  matches any int and any unknown enum case ...
> case default:  … matches anything ...
> }
> 
> Furthermore, if you have a switch that enumerates all of the known cases and 
> use #unknown, then it falls out of the model that new cases (e.g. due to an 
> SDK upgrade or an updated source package) produces the existing build error.  
> As with the original proposal, you can always choose to use “default:” 
> instead of “case #unknown:” if you don’t like that behavior.
> 
> Of course, if you have an exhaustive enum (e.g. one defined in your own 
> module or explicitly marked as such) then #unknown matches nothing, so we 
> should warn about it being pointless.
> 
> 
> This addresses my concerns above:
> 
> 1) This fits into patterns in recursive positions, and slots directly into 
> the existing grammar for patterns.  It would be a very simple extension to 
> the compiler instead of a special case added to switch/case.
> 
> 2) Because it slots into the pattern grammar, it works directly with 'if 
> case’ and the other pattern matching stuff.
> 
> 3) Doesn’t match known cases.
> 
> 4) Doesn’t change the case grammar, it just adds a new pattern terminal 
> production.
> 
> 5) Allows weird cases like the example 

Re: [swift-evolution] Questions about non-exhaustive enums

2018-01-08 Thread Jordan Rose via swift-evolution
Good questions. The answer at a high level is "you break binary and source 
compatibility", and if you care about binary compatibility everything will fall 
to pieces (read: crashes, possibly undefined behavior). So our only goal on the 
binary compatibility is to make the "falling to pieces" as deterministic as 
possible, as described as a "Future direction"; we want library authors to not 
do this by mistake, and if they do we want apps to fail to launch rather than 
potentially corrupting user data.

For libraries that don't care about binary compatibility, breaking source 
compatibility is an option, if not one to be taken lightly. So:

- Adding a case to a frozen enum will result in errors in all switch statements 
without catch-all cases ('default' or '_' patterns), with the diagnostic saying 
that there is now an unhandled pattern. This is essentially the behavior of 
enums in Swift 4.

- Changing an enum from frozen to non-frozen will result in errors in all 
switch statements without catch-all cases, with the diagnostic saying that a 
switch over a non-frozen enum must include a catch-all pattern or `unknown 
case`. This is the diagnostic people will see when migrating from Swift 4 to 
Swift 5, so it has to be good anyway.

I'll add this to the proposal. Thanks, Nacho!

Jordan


> On Jan 5, 2018, at 10:54, Ignacio Soto via swift-evolution 
>  wrote:
> 
> I love the revision to the proposal 
> , but I have a couple of 
> remaining questions that don't seem to be addressed in the doc and I'm 
> curious about:
> What happens if a library maintainer adds a new case to a @frozen enum?
> What happens if a library maintainer changes an enum from @frozen to 
> non-frozen?
> Thanks!
> 
> 
> -- 
> Ignacio Soto
> ___
> 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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-08 Thread Jordan Rose via swift-evolution
It's true that Apple framework authors have this capability, known as a 
"linked-on-or-after check", but it has several caveats:

- It can only check what SDK the application was linked against. Any prebuilt 
libraries that the application uses might have been built against an older or 
newer SDK. (Yes, Apple discourages linking against libraries you didn't build 
yourself; at the same time we all know people do it.)

- The check is "what SDK was the application linked against". That means that a 
developer is opting into all new behavior when they update to a new SDK, 
instead of just one API's.

- The whole system still has to work together, so it's not always possible to 
preserve the old behavior.

- This means that it's not true that "every library actually keeps old 
implementations around". What happens is that individual frameworks may choose 
to do a "linked-on-or-after" check for a particular feature, and modify their 
behavior based on that. They may also choose not to, which might make sense 
for, e.g., a new UIView animation kind, or a new property list serialization 
kind.

I haven't looked at all, but I suspect the vast majority of changes between 
Apple OS releases, including new cases added to enums, are not guarded by 
linked-on-or-after checks. (I probably can't look, or at least can't tell you, 
because anything not release-noted might be left that way for a reason.) You 
may have been thinking of, say, the .NET approach to compatibility 
<https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/versions-and-dependencies>,
 where Microsoft actually does ship several copies of the standard library and 
runtimes when they make major breaking changes, but even then they don't do 
this for every single OS update.


I think Jon's original point has been answered by now: this is an issue in 
Objective-C, but not one that leads to undefined behavior, because in effect 
the compiler treats all C enums as "non-exhaustive" except in warnings. (You 
can imagine a `default: break;` inserted at the end of any switch that doesn't 
otherwise have one in C.)

Jordan


> On Jan 8, 2018, at 11:02, Saagar Jha via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> (Disclaimer: I’m no expert in this, and I know there are people in this list 
> that are, so if there’s anything wrong please feel free to step in and 
> correct me)
> 
> As far as I’m aware, Apple’s frameworks check which version of the framework 
> you linked with at runtime (for UIKit, I believe this function is 
> UIApplicationLinkedOnOrAfter) and modify their behavior to match. For 
> example, if this function shows your application was linked with the iOS 6 
> SDK you’d get the old skeuomorphic UI instead of the “flatter”, newer iOS 
> 7-style controls. What this means is every library actually keeps old 
> implementations around in addition to the newer ones and picks the necessary 
> one at runtime, so binary compatibility boils down to things like “don't 
> change the memory layout of classes” rather than “don’t change the behavior 
> of your program”.
> 
> Saagar Jha
> 
>> On Jan 5, 2018, at 19:11, Jon Shier via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> At this point I think it might be useful to outline how binary compatibility 
>> works for Objective-C on Apple platforms right now. As an app developer I’m 
>> not intimately familiar with what happens when you run an app compiled with 
>> the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code 
>> paths or something else? The more this thread goes on the more confused I 
>> get about why Swift would have this issue while it doesn’t appear to be one 
>> for Obj-C. If an enum adds a case now, I don’t have to care until I 
>> recompile using the new SDK. Is the intention for Swift to be different in 
>> this regard?
>> 
>> 
>> 
>> Jon Shier
>> 
>> On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>>> 
>>> 
>>>> On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>> 
>>>> Is it hard to imagine that most everyone can get what they want and keep 
>>>> the syntax clean and streamlined at the same time? Without any "@" signs 
>>>> or other compiler hints?
>>> 
>>> For what it's worth, the original version of the proposal started with a 
>>> modifier (a context-sensitive keyword, like 'final'), but the core team 
>>> felt t

Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-05 Thread Jordan Rose via swift-evolution


> On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution 
>  wrote:
> 
> Is it hard to imagine that most everyone can get what they want and keep the 
> syntax clean and streamlined at the same time? Without any "@" signs or other 
> compiler hints?

For what it's worth, the original version of the proposal started with a 
modifier (a context-sensitive keyword, like 'final'), but the core team felt 
that there were a lot of modifiers in the language already, and this didn't 
meet the bar.


> "Rather, we are how to enable the vendor of a nonexhaustive enum to add new 
> cases without breaking binaries compiled against previous versions"
> 
> When an enum changes, and the change causes the code to break, the user can 
> be presented with migration options from an automated IDE tool. In what 
> specific way does this not solve the issue about having to upgrade your code 
> when using someone else's code library? This very notion implies your 
> disgruntled about doing work when things are upgraded, is that really what 
> this fuss is all about?
> 
> A well written language interpreter and auto-tooling IDE would not need hints 
> embedded in the code syntax itself. Migration hints from version to version 
> should not be a part of either the past or future version of the code library.

Thanks for bringing this up! Unfortunately, it falls down in practice, because 
if there's a new enum case, it's unclear what you want to do with it. If you're 
handling errors, it's not obvious that the way you've handled any of the other 
errors is appropriate. In the (admittedly controversial) 
SKPaymentTransactionState case, none of the existing code would be appropriate 
to handle the newly-introduced "deferred" case, and nor could StoreKit provide 
"template" code that would be appropriate to the client app.


In any case, though, the key point on this particular quoted sentence is 
"without breaking binaries". Any such change must be valid without 
recompilation, and indeed without any intervention from the developer or an 
IDE, because that's what happens when the user updates their OS.

Jordan



> 
> ...
> 
> I don't expect the community to agree on language grammar, but the common 
> sense here on how to achieve the intended goals seems to be out of wack.
> 
> If someone can present a clear logical statement as to how an automated 
> migration tool behind the scenes in the IDE to handle all your versioning 
> worries, does not make this whole discussion about adding more convoluted 
> syntax additions irrelevant, I'd love to hear it.
> 
> ___
> 
> Sincerely,
> Jason
> 
> 
> 
> 
> 
> 
> On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu  > wrote:
> On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution 
> > wrote:
> I think this whole thing has been unnecessarily convoluted. As a result, the 
> majority of the replies are rabbit holes.
> 
> In my opinion, the true root of the concept in question is as follows:
> 
> A list of something is desired:
> 1 - Pancake
> 2 - Waffle
> 3 - Juice
> 
> Developer wishes to be able to:
> A) Add new things to the list of choices in the future as they come up with 
> new ideas
> B) Sometimes select one of the choices to be chosen as the normal choice if 
> no choice is made by the user
> 
> A and B are separate desires. In some circumstances a developer may want to 
> add a new choice and make it the normal choice when there was no normal 
> choice was clarified before.
> 
> I don't think this is an accurate summary of the problem being tackled here. 
> Rather, we are how to enable the vendor of a nonexhaustive enum to add new 
> cases without breaking binaries compiled against previous versions. There is 
> little here to do with what a "default" should be. Indeed, it is an explicit 
> design decision of Swift not to support types having an implicit default 
> value.
>  
> 
> 
> Part 2:
> 
> After this simple desire is clear, there should be two discussions:
> A) In a text only coding language, what would we like the syntax to look 
> like? (Without regard to past-bias. What should it really be, forget what 
> mistaken design choices were made in Swift in the past)
> B) How do we approach making this happen behind the scenes?
> 
> Bonus: Given that some of us have changed our approach to programming 
> significantly beyond text based coding, and into more dynamic mediums of 
> programming in other niches, and even here and there in Xcode - I would 
> recommend considering how the IDE would show a modern version of this 
> concept. I feel too often that Swift design syntax has a lack of awareness 
> between the distinctions of what the IDE should do, as opposed to what the 
> syntax of the language should be, and what should be handled behind the 
> scenes by automated tooling.
> 
> 

Re: [swift-evolution] [swift-evolution-announce] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-05 Thread Jordan Rose via swift-evolution
Clarifications here only! I don't think I have too much more to convince you; 
at this point we've looked at the same landscape and come to different 
conclusions. Although I think I'm more optimistic than you are about being able 
to build a binary compatibility checker.


> On Jan 5, 2018, at 06:52, Swift  wrote:
> 
>> They can't tell themselves whether your app supports the new case, other 
>> than the heavy-handed "check what SDK they compiled against" that ignores 
>> the possibility of embedded binary frameworks. 
> 
> To be fair, a “linked on or after” check isn’t that heavy-handed. It’s a 
> single if-statement. Yes, they become unwieldy if you’ve got them 
> *everywhere* in your library code, which is why I brought up the point 
> earlier that I’d really love to see more in the way of facilitating this sort 
> of link-dependent behavior that apps are expecting.

Just to clear up this point, I meant "heavy-handed" in that it's just an 
approximation of whether the client is actually ready to deal with new 
complexity. It's not an unreasonable one, but it still forces clients to 
potentially think about all the new things if they just want to update to use 
one of them.

Maybe "coarse-grained" would have been better phrasing. That also covers the 
"on or off for the entire app" aspect.


>>> Teaching the compiler/checker/whatever about the linking semantics of 
>>> modules. For modules that are packaged inside the final built product, 
>>> there is no need to deal with any unexpected cases, because we already have 
>>> the exhaustiveness check appropriate for that scenario (regardless of 
>>> whether the module is shipped as a binary or compiled from source). The app 
>>> author decides when to update their dependencies, and updating those 
>>> dependencies will produce new warnings/errors as the compiler notices new 
>>> or deprecated cases. This is the current state of things and is completely 
>>> orthogonal to the entire discussion.
>> 
>> This keeps sneaking into discussions and I hope to have it formalized in a 
>> proposal soon. On the library side, we do want to make a distinction between 
>> "needs binary compatibility" and "does not need binary compatibility". Why? 
>> Because we can get much better performance if we know a library is never 
>> going to change. A class will not acquire new dynamic-dispatch members; a 
>> stored property will not turn into a computed property; a struct will not 
>> gain new stored properties. None of those things affect how client code is 
>> written, but they do affect what happens at run-time.
> 
> This is incorrect; binary compatibility absolutely affects how code is 
> written. No swift library available today is written with binary 
> compatibility in mind, because it’s a non-issue. If it were a pervasive 
> issue, then you’d see a lot more diligence in libraries about not straight-up 
> breaking things between releases.
> 
> Other responses on this thread have mentioned removing implementations 
> between releases, which is an excellent illustration of this point: when 
> writing a binary-compatible library, *you cannot remove implementations*. So 
> the very fact that people think that it’s ok to do so is an explicit 
> refutation of the assertion that the manner in which client code is written 
> is independent of the consideration of a library needing binary compatibility.

I very carefully said "client", and then Ted reminded me yesterday in another 
context that that's not a standard term. So to be clear, I meant "none of these 
performance-related things would change what an app can and can't do with the 
API of the library", not anything broader about the design of the library or 
even about the design of the app.

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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-05 Thread Jordan Rose via swift-evolution


> On Jan 5, 2018, at 00:11, Jonathan Hull via swift-evolution 
>  wrote:
> 
>> 
>> On Jan 4, 2018, at 11:02 PM, Xiaodi Wu > > wrote:
>> 
>> 
>> On Fri, Jan 5, 2018 at 01:56 Jonathan Hull > > wrote:
>>> On Jan 4, 2018, at 10:31 PM, Xiaodi Wu via swift-evolution 
>>> > wrote:
>> 
>>> 
>>> On Fri, Jan 5, 2018 at 00:21 Cheyo Jimenez >> > wrote:
>> 
>>> 
>>> On Jan 4, 2018, at 4:37 PM, Xiaodi Wu >> > wrote:
>>> 
>> 
 On Thu, Jan 4, 2018 at 19:29 Cheyo J. Jimenez > wrote:
 
 We seem to agree that, by virtue of not supporting use in a pattern and 
 being placed at the end, the feature is a flavor of default. I’m still not 
 sure I understand why you believe it should not be a flavor of default 
 going forward.
 
> 
> You still haven’t answered my question, though—what’s the use case for 
> the feature you propose?
 
 My use case would be distinguishing between compile time known cases vs 
 “future only” cases (or unknown cases).
 
 I understand that the feature you propose would allow you to make such a 
 distinction, but again, what is your use case for doing so?
>> 
>>> Breaking out early by checking unknown cases first. I admit this is not 
>>> deal breaker, just a different style I’d like to see supported in the 
>>> future. 
>> 
>>> 
>>> I'm still not sure I understand. How can the machine know that it's dealing 
>>> with an unknown case without first checking if it matches any known case?
>> 
>> 
>> I had the same thought as Cheyo.  It isn’t a deal breaker… I like the 
>> compromise, but I would prefer it trigger only on an actual unknown case (as 
>> opposed to acting like default). I like to break failure cases out at the 
>> top when possible. I don’t see any good reason not to support that style.
>> 
>> To answer your question, in the naive sense, it basically is the same 
>> question as asking if it is a known case (and then taking the inverse). That 
>> doesn’t mean actually checking each case separately though. For example, if 
>> the enum cases are internally represented as an unsigned integer, and they 
>> are all together in a block, the compiler could simply check that it is 
>> greater than the max known value. You could probably even do a bit mask 
>> comparison in some cases...
>> 
>> These are obvious optimizations, but why does this require new syntax?
> 
> I am not sure I understand what you are asking. There isn’t additional 
> syntax.  We are just arguing over the name + behavior of ‘unexpected:’.  You 
> want it to behave like ‘default’ and I am saying that stops the use case I 
> mention above.
> 
> 
>> What do you gain from writing the unknown case first?
> I know where to look for the failure cases.  I also tend put a bunch of guard 
> statements near the beginning of a function.  It is just a programming style.
> 
> With my behavior of ‘unexpected:’ you can put it wherever you want.  Why 
> limit that by forcing it to go at the end?
> 
>> Isn't this basically the same thing as asking for the ability to write the 
>> default case first, a frequently suggested and rejected syntax addition?
> 
> No.  I don’t think I have ever heard that asked for, but putting default in a 
> different place has a different meaning.  The way I read a switch statement 
> anyway is that it tries each case until it find one that matches.  Default 
> matches everything, so it has to go at the end (since it will always match 
> and nothing afterwards will be tried).
> 
> Having ‘unexpected:’ also match known/expected cases is problematic as a 
> mental model.  I think that is just an artifact of the original proposal 
> using default.  There is no reason 'unexpected:’ should have to handle known 
> cases as well… let’s just have it trigger on unexpected ones.

I'm going to repeat this from my reply to Cheyo earlier: I really, really don't 
want recompiling code against a different version of the library to pick a 
different case than it did before.

(This might be a weak argument since overload resolution, protocol conformance 
checking, etc can end up picking a different declaration than it did before. 
But I would hope that the overloads or alternate protocol witnesses at least 
"do the same thing" in normal situations, if possibly more efficiently. I 
wouldn't expect that for `unknown case` vs. `default` if you actually had both 
of them.)


The reason `unknown case` has to match known cases is, again, for source 
compatibility. If the compiler only produces a warning, rather than an error, 
when you're missing a case, it has to do something if that case comes up. The 
most reasonable thing for it to do (in 

Re: [swift-evolution] [swift-evolution-announce] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-04 Thread Jordan Rose via swift-evolution
at can update independently of 
> the final app, and therefore there are two possible cases:
> 
>   1️⃣ If the enum is decorated with @frozen, then I, as an app author, 
> have the assurance that the enum case will not change in future releases of 
> the library, and I can safely switch on all known cases and not have to 
> provide a default case. 
> 
>   2️⃣ If the enum is NOT decorated with @frozen, then I, as an app 
> author, have to account for the possibility that the module may update from 
> underneath my app, and I have to handle an unknown case. This is simple: the 
> compiler should require me to add a “default:” case to my switch statement. 
> This warning is produced IFF: the enum is coming from an external module, and 
> the enum is not decorated with @frozen.
> 
> 
> ==
> 
> With this proposal, we only have one thing to consider: the spelling of 
> @frozen/@moana/@whatever that we decorate enums in external modules with. 
> Other than that, the existing behavior we currently have is completely 
> capable of covering the possibilities: we just keep using a “default:” case 
> whenever the compiler can’t guarantee that we can be exhaustive in our 
> switching.
> 
> Where the real work would be is teaching the compiler about 
> internally-vs-externally linked modules.
> 
> Dave
> 
>> On Jan 2, 2018, at 7:07 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> [Proposal: 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>>  
>> <https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md>]
>> 
>> Whew! Thanks for your feedback, everyone. On the lighter side of 
>> feedback—naming things—it seems that most people seem to like '@frozen', and 
>> that does in fact have the connotations we want it to have. I like it too.
>> 
>> More seriously, this discussion has convinced me that it's worth including 
>> what the proposal discusses as a 'future' case. The key point that swayed me 
>> is that this can produce a warning when the switch is missing a case rather 
>> than an error, which both provides the necessary compiler feedback to update 
>> your code and allows your dependencies to continue compiling when you update 
>> to a newer SDK. I know people on both sides won't be 100% satisfied with 
>> this, but does it seem like a reasonable compromise?
>> 
>> The next question is how to spell it. I'm leaning towards `unexpected 
>> case:`, which (a) is backwards-compatible, and (b) also handles "private 
>> cases", either the fake kind that you can do in C (as described in the 
>> proposal), or some real feature we might add to Swift some day. `unknown 
>> case:` isn't bad either.
>> 
>> I too would like to just do `unknown:` or `unexpected:` but that's 
>> technically a source-breaking change:
>> 
>> switch foo {
>> case bar:
>>   unknown:
>>   while baz() {
>> while garply() {
>>   if quux() {
>> break unknown
>>   }
>> }
>>   }
>> }
>> 
>> Another downside of the `unexpected case:` spelling is that it doesn't work 
>> as part of a larger pattern. I don't have a good answer for that one, but 
>> perhaps it's acceptable for now.
>> 
>> I'll write up a revision of the proposal soon and make sure the core team 
>> gets my recommendation when they discuss the results of the review.
>> 
>> ---
>> 
>> I'll respond to a few of the more intricate discussions tomorrow, including 
>> the syntax of putting a new declaration inside the enum rather than outside. 
>> Thank you again, everyone, and happy new year!
>> 
>> Jordan
>> 
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto: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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-04 Thread Jordan Rose via swift-evolution


> On Jan 4, 2018, at 11:45, Cheyo Jimenez  wrote:
> 
> 
> 
> On Jan 4, 2018, at 10:49 AM, Jordan Rose  > wrote:
> 
>> I'll admit I hadn't thought of using "unknown default" (or "default 
>> unknown"). I don't think that's terrible, but I mildly prefer `unknown case` 
>> because it builds on the "pun" that enum elements are also defined using 
>> 'case'. If anything hits this part of the switch, it really will be an 
>> "unknown case", i.e. a statically-unknown enum element.
>> 
>> To Cheyo's point, if this were to be a single token I'd probably spell it 
>> #unknown, like #available. Then we'd have `case #unknown:` and something 
>> that naturally expands to other pattern positions. I found that less 
>> aesthetically pleasing, though, and so a context-sensitive keyword seemed 
>> like the way to go.
>> 
>> (For the record, though, I wouldn't describe `case _` as a special case of 
>> `default`. They do exactly the same thing, and `_` is a useful pattern in 
>> other contexts, so if anything the current `default` should be thought of as 
>> syntactic sugar for `case _`.)
> 
> Can case _ be mixed with unknown case? How can we match all compile time 
> known cases but exclude future cases? Should be something like `case *` that 
> would capture all currently known cases during compile time? case * and case 
> _ would be the same in exhaustive enums. 

I thought about this, but I couldn’t think of a use case for it. You won’t get 
any exhaustivity checks, and if you recompile the app you silently get 
different behavior. That didn’t seem like a good idea to me.

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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-04 Thread Jordan Rose via swift-evolution
I'll admit I hadn't thought of using "unknown default" (or "default unknown"). 
I don't think that's terrible, but I mildly prefer `unknown case` because it 
builds on the "pun" that enum elements are also defined using 'case'. If 
anything hits this part of the switch, it really will be an "unknown case", 
i.e. a statically-unknown enum element.

To Cheyo's point, if this were to be a single token I'd probably spell it 
#unknown, like #available. Then we'd have `case #unknown:` and something that 
naturally expands to other pattern positions. I found that less aesthetically 
pleasing, though, and so a context-sensitive keyword seemed like the way to go.

(For the record, though, I wouldn't describe `case _` as a special case of 
`default`. They do exactly the same thing, and `_` is a useful pattern in other 
contexts, so if anything the current `default` should be thought of as 
syntactic sugar for `case _`.)

I'll add these points to the "Alternatives Considered" section in the PR later 
today.

Jordan


> On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi...@gmail.com> wrote:
> 
> As has already been said, “case unknown” is source-breaking because it 
> conflicts with any real cases named “unknown”; “\unknown” looks like a key 
> path but isn’t, and I wonder if it would potentially conflict with existing 
> key paths.
> 
> In any case, my point was not to bikeshed the “unknown” part, but to ask 
> whether any consideration had been made to have the feature presented as a 
> flavor of default instead of a flavor of case.
> 
> On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <ch...@masters3d.com 
> <mailto:ch...@masters3d.com>> wrote:
> 
> 
> On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> 
>> This is a very nice revision. One bikeshedding thought:
>> 
>> Since "unknown case" is presented as a special kind of "default", can't be 
>> mixed with "default", and can't be used in case patterns, why not "default 
>> unknown" (or "unknown default") instead of "unknown case"?
> 
> `case _ :` is already a special case of default. 
> I’d rather have `case unknown :`
> `unknown case :` is weird because of the order of `case`. 
> 
> Another alternative is `case \unknown :`
> `\unknown` would also allow pattern matching. 
> 
> 
> 
>> 
>> 
>> On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> On Jan 2, 2018, at 18:07, Jordan Rose <jordan_r...@apple.com 
>>> <mailto:jordan_r...@apple.com>> wrote:
>>> 
>>> [Proposal: 
>>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>>>  
>>> <https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md>]
>>> 
>>> Whew! Thanks for your feedback, everyone. On the lighter side of 
>>> feedback—naming things—it seems that most people seem to like '@frozen', 
>>> and that does in fact have the connotations we want it to have. I like it 
>>> too.
>>> 
>>> More seriously, this discussion has convinced me that it's worth including 
>>> what the proposal discusses as a 'future' case. The key point that swayed 
>>> me is that this can produce a warning when the switch is missing a case 
>>> rather than an error, which both provides the necessary compiler feedback 
>>> to update your code and allows your dependencies to continue compiling when 
>>> you update to a newer SDK. I know people on both sides won't be 100% 
>>> satisfied with this, but does it seem like a reasonable compromise?
>>> 
>>> The next question is how to spell it. I'm leaning towards `unexpected 
>>> case:`, which (a) is backwards-compatible, and (b) also handles "private 
>>> cases", either the fake kind that you can do in C (as described in the 
>>> proposal), or some real feature we might add to Swift some day. `unknown 
>>> case:` isn't bad either.
>>> 
>>> I too would like to just do `unknown:` or `unexpected:` but that's 
>>> technically a source-breaking change:
>>> 
>>> switch foo {
>>> case bar:
>>>   unknown:
>>>   while baz() {
>>> while garply() {
>>>   if quux() {
>>> break unknown
>>>   }
>>> }
>>>   }
>>> }
>>> 
>>> Another downside of the `unexpected case:` s

Re: [swift-evolution] [swift-evolution-announce] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-03 Thread Jordan Rose via swift-evolution
> On Jan 2, 2018, at 18:07, Jordan Rose  wrote:
> 
> [Proposal: 
> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>  
> ]
> 
> Whew! Thanks for your feedback, everyone. On the lighter side of 
> feedback—naming things—it seems that most people seem to like '@frozen', and 
> that does in fact have the connotations we want it to have. I like it too.
> 
> More seriously, this discussion has convinced me that it's worth including 
> what the proposal discusses as a 'future' case. The key point that swayed me 
> is that this can produce a warning when the switch is missing a case rather 
> than an error, which both provides the necessary compiler feedback to update 
> your code and allows your dependencies to continue compiling when you update 
> to a newer SDK. I know people on both sides won't be 100% satisfied with 
> this, but does it seem like a reasonable compromise?
> 
> The next question is how to spell it. I'm leaning towards `unexpected case:`, 
> which (a) is backwards-compatible, and (b) also handles "private cases", 
> either the fake kind that you can do in C (as described in the proposal), or 
> some real feature we might add to Swift some day. `unknown case:` isn't bad 
> either.
> 
> I too would like to just do `unknown:` or `unexpected:` but that's 
> technically a source-breaking change:
> 
> switch foo {
> case bar:
>   unknown:
>   while baz() {
> while garply() {
>   if quux() {
> break unknown
>   }
> }
>   }
> }
> 
> Another downside of the `unexpected case:` spelling is that it doesn't work 
> as part of a larger pattern. I don't have a good answer for that one, but 
> perhaps it's acceptable for now.
> 
> I'll write up a revision of the proposal soon and make sure the core team 
> gets my recommendation when they discuss the results of the review.
> 
> ---
> 
> I'll respond to a few of the more intricate discussions tomorrow, including 
> the syntax of putting a new declaration inside the enum rather than outside. 
> Thank you again, everyone, and happy new year!

I ended up doing these in the opposite order, writing up the new proposal first 
and not yet responding to the discussion that's further out. You can read my 
revisions at https://github.com/apple/swift-evolution/pull/777.

In particular, I want to at least address:
- Dave D and Drew C's points about versioned libraries / linking semantics of 
modules.
- Jason M's point about migration
and I'll do one more pass over the thread to see if there's anything else I 
didn't address directly. (That doesn't mean everyone who disagrees, just 
messages where I think there's more I can do to explain why the proposal is the 
way it is.)

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] [swift-evolution-announce] [Review] SE 0192 - Non-Exhaustive Enums

2018-01-02 Thread Jordan Rose via swift-evolution
[Proposal: 
https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
 
]

Whew! Thanks for your feedback, everyone. On the lighter side of 
feedback—naming things—it seems that most people seem to like '@frozen', and 
that does in fact have the connotations we want it to have. I like it too.

More seriously, this discussion has convinced me that it's worth including what 
the proposal discusses as a 'future' case. The key point that swayed me is that 
this can produce a warning when the switch is missing a case rather than an 
error, which both provides the necessary compiler feedback to update your code 
and allows your dependencies to continue compiling when you update to a newer 
SDK. I know people on both sides won't be 100% satisfied with this, but does it 
seem like a reasonable compromise?

The next question is how to spell it. I'm leaning towards `unexpected case:`, 
which (a) is backwards-compatible, and (b) also handles "private cases", either 
the fake kind that you can do in C (as described in the proposal), or some real 
feature we might add to Swift some day. `unknown case:` isn't bad either.

I too would like to just do `unknown:` or `unexpected:` but that's technically 
a source-breaking change:

switch foo {
case bar:
  unknown:
  while baz() {
while garply() {
  if quux() {
break unknown
  }
}
  }
}

Another downside of the `unexpected case:` spelling is that it doesn't work as 
part of a larger pattern. I don't have a good answer for that one, but perhaps 
it's acceptable for now.

I'll write up a revision of the proposal soon and make sure the core team gets 
my recommendation when they discuss the results of the review.

---

I'll respond to a few of the more intricate discussions tomorrow, including the 
syntax of putting a new declaration inside the enum rather than outside. Thank 
you again, everyone, and happy new year!

Jordan

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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-21 Thread Jordan Rose via swift-evolution


> On Dec 20, 2017, at 12:54, Charlie Monroe  wrote:
> 
> I think that the main confusion here stems from the word library as we are 
> addressing something that can be divided further (and this is IMHO as many 
> macOS/iOS devs see it):
> 
> - libraries that come with the OS - here, it absolutely makes sense to make 
> the enums non-exhaustive as the apps are linked against these libraries and 
> the user installs a binary that will load these at launch and they are not 
> bundled with the app - the developer can't control future OS releases and he 
> wants the app to run on a future OS release.
> - libraries that are bundled with the app - be it PM, CocoaPods or something 
> else - you typically update your dependencies once in a while and they 
> change. And you want to be notified by the compiler about possible changes - 
> extended enums, in this case. Because let's be honest - if your app has a 
> dozen dependencies and you come to the app after a year of no development, 
> Swift 5 came along during that period, you want to update these libraries to 
> Swift-5-compatible versions. And no one has the time to go through all change 
> logs - even if they were kept up-to-date and thorough, which I can't say that 
> I've seen in many instances.
> 
> I know that this is a limited view from the perspective of an app developer 
> and that potentially, e.g. on Linux, there may be libraries written in Swift 
> that you may want to install via package managers and depend on them once the 
> ABI is stable, but the choice to make them non-exhaustive by default is not 
> in line with everything else in Swift - everything else is generally closed 
> by default - public (-> final in other modules), no access modified (-> 
> internal), ...
> 
> For me, it's a -1 as it is now. I'd prefer exhaustive-by-default, 
> ObjC/C-import non-exhaustive by default (the way ObjC classes are open by 
> default vs. public). When it comes to the switch statement, there definitely 
> needs to be an option to make an exhaustive switch over all 
> compile-time-known values with a warning shall a new one be added. Without 
> that, the code will become incredibly prone to errors and hard to maintain.

This does bring up another option, which is to differentiate Apple-provided 
libraries (and in general, libraries with binary compatibility concerns, i.e. 
libraries that may be different at run-time from what you compiled against) 
from bundled / built-from-source libraries. Slava and I have been very leery of 
this because it fragments the language into dialects, and has the potential to 
confuse people when they try to write code that behaves like an Apple 
framework, but I suppose it is an option.

Jordan


> 
>> On Dec 20, 2017, at 9:35 PM, Karl Wagner via swift-evolution 
>> > wrote:
>> 
>> 
>> 
>>> On 20. Dec 2017, at 19:54, Jordan Rose >> > wrote:
>>> 
>>> 
>>> 
 On Dec 20, 2017, at 05:36, Karl Wagner via swift-evolution 
 > wrote:
 
 
 
> On 19. Dec 2017, at 23:58, Ted Kremenek via swift-evolution 
> > wrote:
> 
> The review of "SE 0192 - Non-Exhaustive Enums" begins now and runs 
> through January 3, 2018.
> 
> The proposal is available here:
> 
> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>  
> +1,
>  it needs to happen (and ASAP, since it _will_ introduce source-breaking 
> changes one way or the other).
 
 I think non-exhaustive is the correct default. However, does this not mean 
 that, by default, enums will be boxed because the receiver doesn’t know 
 their potential size?
>>> 
>>> It's not always boxing, but yes, there will be more indirection if the 
>>> compiler can't see the contents of the enum. (More on that below.)
>>> 
>>> 
 That would mean that the best transition path for multi-module Apps would 
 be to make your enums @exhaustive, rather than adding “default” statements 
 (which is unfortunate, because I imagine when this change hits, the way 
 you’ll notice will be complaints about missing “default” statements).
>>> 
>>> Yep, that's going to be the recommendation. The current minimal-for-review 
>>> implementation does not do this but I'd like to figure out how to improve 
>>> that; at the very least it might be a sensible thing to do in the migrator.
>>> 
 
 I do have some thoughts about how we could ease the transition (for this 
 and other resilience-related changes), but it’s best to leave that to a 
 separate discussion.
 
 The one thing I’m still not overly fond of is the name - I 

Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-21 Thread Jordan Rose via swift-evolution


> On Dec 20, 2017, at 12:52, Colin Barrett  wrote:
> 
> 
>> On Dec 20, 2017, at 1:36 PM, Jordan Rose > > wrote:
>> 
>> Thanks for the links, Colin. I think neither of these approaches are 
>> appropriate for Swift, largely for the same reason: they can't be used to 
>> define a library's API. Polymorphic variants are ad-hoc union types (much 
>> like tuples are ad-hoc structs) which—apart from having other problems in 
>> Swift previously discussed on this list—means that you can't add new cases 
>> to them. That is, any API which takes `(red(r: Int) | green(g: Int) | 
>> blue(b: Int))` today can't add `alpha(a: Int)` in the future, because that 
>> would change the type.
>  
> It would change the type yes, but not in a binary incompatible way. Imagine 
> this for the OS version, using OCaml pseudocode
> 
> type VERS = [> `10_0 | `10_1 | … | `10_13 ]
> 
> Then, next year, you’d change VERS to be,
> 
> type VERS = [> `10_0 | `10_1 | … | `10_13 | `10_14 ]
> 
> Any code that dealt with a VERS would still work, as it had to handle that it 
> could contain other tags.

Sorry, I had this worked out in my head but then didn't put it into the email 
correctly! The sticking point is that Swift allows overloads by type, which 
means that argument types have to go into the mangled name of the function. 
(Another way to think of this is to say "to uniquely identify a function across 
releases, you must know its argument and return types as well as its full 
name".) That means that we can't just add a new case into the type, because it 
would change the function's type.

Okay, but what if VERS were an actual new type rather than just a typealias? 
Well, then you have a non-exhaustive enum.

(David Owens brought up on Twitter today that it could be a different kind of 
declaration rather than 'enum'. My response 
 was that that could 
work, but you end up in a worse case where (a) the two declarations are 
equivalent when they're not public, and (b) it's still easy to use the wrong 
one, especially going from Swift 1-4 habits.)


> 
>> ML-style exceptions have the opposite problem; they are completely 
>> unconstrained and so a client can add new "cases" without the library author 
>> being prepared. (Swift's error-handling model in general behaves a lot like 
>> this, except it doesn't get ML's knowledge of which errors might be thrown.)
> 
> Yes, I was imagining that this would be for something with an OCaml type like 
> [> ]  or TOP, which I don’t believe is a thing? My OCaml-fu is quite weak.
> 
>> I'd sum this up by saying Swift is differing from ML and from most other 
>> languages because it is solving a different problem. Swift is designed such 
>> that the compiler does not require whole-program knowledge to produce a 
>> correct, working, type-safe program. It will use any cross-module knowledge 
>> it can for optimization purposes, but the language semantics do not depend 
>> on it. And this is critical, as you note, for libraries with binary 
>> compatibility concerns.
> 
> That is… not different from ML? ML’s modules have precisely this properly, do 
> they not? Or am I misunderstanding what you’re saying here.

ML modules provide separation of concerns, but as far as I know they can't 
actually be swapped out at runtime, and if someone were to do this they 
wouldn't be allowed to add a new variant to an existing datatype.


> Thanks for the reply, it’s appreciated! Hope you’re well in California, 
> envious of your weather trudging thru the snow here in NYC.

Happy holidays, Colin, and everyone else on the list. :-)

Jordan


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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-21 Thread Jordan Rose via swift-evolution
Thanks for your comments, Jon. Responses inline.

> On Dec 20, 2017, at 12:46, Jonathan Hull  wrote:
> 
> 
>> On Dec 19, 2017, at 2:58 PM, Ted Kremenek via swift-evolution 
>> > wrote:
>> When reviewing a proposal, here are some questions to consider:
>> 
>> What is your evaluation of the proposal?
>> 
> Strong -1 as is.
> 
> I think I would support having an explicit ‘futureCase’ which is different 
> from ‘default’.  Requiring default is asking for bugs.  With a separate 
> ‘futureCase’, we would still get a compile-time error when all of the current 
> cases aren’t handled.  Note that ‘ futureCase’ is also different than 
> ‘default’ in that it wouldn’t have to go at the bottom.  It would only be 
> triggered when the value isn’t one of the values which was known at compile 
> time.  We should also bike shed the name of ‘futureCase’ to come up with 
> something that might allow us to make other types of extendable enums….  
> Maybe something like ‘unknownCase’ or ‘unexpectedCase’.
> 
> As for the issue of testing, we could add (later) a universally unexpected 
> case that non-exhaustive enums can be set to for testing. I am not convinced 
> that this is actually a big enough issue to warrant that though. Forcing 
> ‘default’ is a much larger real-world problem, IMO (and this use of ‘default’ 
> is just as untestable).

Both of these are discussed in "Alternatives considered", and they have flaws 
that led me to leave them out of the proposal. Do you have any ideas on how to 
improve on that?

> 
> I also dislike the name @exhaustive because I think it will cause confusion 
> (i.e. “Of course my Enum is exhaustive”).  I think we should just have @fixed 
> for both enums and structs. The word fixed has the connotation that it 
> shouldn’t change.

I don't think anybody loves "exhaustive", but using the same attribute for 
enums and structs isn't really appropriate, as discussed in my response to Karl 
yesterday 
.
 I'm not opposed to a better name if we can gravitate towards one, though.

Jordan



> 
>> Is the problem being addressed significant enough to warrant a change to 
>> Swift?
>> 
> Yes, I think the problem is significant, but this is not the correct solution.
>> Does this proposal fit well with the feel and direction of Swift?
>> 
> Not quite.  While I think ABI safety is Swifty, I feel like this design 
> actually encourages a lack of safety in other areas.  Much better to 
> explicitly deal with the issue of added/future cases in code than to hide it 
> with ‘default’.
> 
>> If you have used other languages or libraries with a similar feature, how do 
>> you feel that this proposal compares to those?
>> 
> I have used Enums in several languages, but Swift is the only one which has 
> required exhaustive enums… I have a hard time going back to languages which 
> don’t have it now.
> 
>> How much effort did you put into your review? A glance, a quick reading, or 
>> an in-depth study?
>> 
> Thoroughly read proposal, casually followed the original discussion.
> 
> Thanks,
> Jon
> 
> 

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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-21 Thread Jordan Rose via swift-evolution


> On Dec 20, 2017, at 12:35, Karl Wagner  wrote:
> 
> 
> 
>> On 20. Dec 2017, at 19:54, Jordan Rose > > wrote:
>> 
>> 
>> 
>>> On Dec 20, 2017, at 05:36, Karl Wagner via swift-evolution 
>>> > wrote:
>>> 
>>> 
>>> 
 On 19. Dec 2017, at 23:58, Ted Kremenek via swift-evolution 
 > wrote:
 
 The review of "SE 0192 - Non-Exhaustive Enums" begins now and runs through 
 January 3, 2018.
 
 The proposal is available here:
 
 https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
  
 +1,
  it needs to happen (and ASAP, since it _will_ introduce source-breaking 
 changes one way or the other).
>>> 
>>> I think non-exhaustive is the correct default. However, does this not mean 
>>> that, by default, enums will be boxed because the receiver doesn’t know 
>>> their potential size?
>> 
>> It's not always boxing, but yes, there will be more indirection if the 
>> compiler can't see the contents of the enum. (More on that below.)
>> 
>> 
>>> That would mean that the best transition path for multi-module Apps would 
>>> be to make your enums @exhaustive, rather than adding “default” statements 
>>> (which is unfortunate, because I imagine when this change hits, the way 
>>> you’ll notice will be complaints about missing “default” statements).
>> 
>> Yep, that's going to be the recommendation. The current minimal-for-review 
>> implementation does not do this but I'd like to figure out how to improve 
>> that; at the very least it might be a sensible thing to do in the migrator.
>> 
>>> 
>>> I do have some thoughts about how we could ease the transition (for this 
>>> and other resilience-related changes), but it’s best to leave that to a 
>>> separate discussion.
>>> 
>>> The one thing I’m still not overly fond of is the name - I would like us to 
>>> keep the set of resilience/optimisation related keywords to a minimum. 
>>> “exhaustive” for enums feels an awful lot like “fixed_contents” for structs 
>>> - couldn’t we come up with a single name which could be used for both? I 
>>> don’t think anybody’s going to want to use “exhaustive” for structs.
>> 
>> The core team was very focused on this too, but I contend that "exhaustive" 
>> is not about optimization and really isn't even about "resilience" (i.e. the 
>> ability to evolve a library's API while preserving binary compatibility). 
>> It's a semantic feature of an enum, much like 'open' or 'final' is for 
>> classes, and it affects what a client can or can't do with an enum. For 
>> libaries compiled from source, it won't affect performance at all—the 
>> compiler still knows the full set of cases in the current version of the 
>> library even if the programmer is forced to consider future versions.
>> 
>> I'm working on the fixed-contents proposal now, though it won't be ready for 
>> a while, and the same thing applies there: for structs compiled from source, 
>> the compiler can still do all the same optimizations. It's only when the 
>> library has binary compatibility concerns that we need to use extra 
>> indirection, and then "fixed-contents" becomes important. (As currently 
>> designed, it doesn't affect what clients can do with the struct at all.) 
>> This means that I don't expect a "normal" package author to write 
>> "fixed-contents" at all (however it ends up being spelled), whereas 
>> "exhaustive" is a fairly normal thing to consider whenever you make an enum 
>> public.
>> 
>> I hope that convinces you that "fixed-contents" and "exhaustive" don't need 
>> to have the same name. I don't think anyone loves the particular name 
>> "exhaustive", but as you see in the "Alternatives considered" we didn't 
>> manage to come up with anything significantly better. If reviewers all 
>> prefer something else we'd consider changing it.
>> 
>> Thanks for responding!
>> Jordan
>> 
> 
> When you say “libraries compiled from source”, what do you mean?

- Other targets in your project
- Source packages built through SwiftPM / CocoaPods / Carthage / other

And I was being imprecise with the terminology, but also

- Libraries built by someone else but designed to be embedded into an app, so 
that there's no chance of a different version showing up at run-time.

> 
> As for whether its a resilience feature: actually it is completely a 
> resilience feature. The effects on switching are only side-effects; really 
> what “exhaustive” or “nonexhaustive” are saying is literally that cases may 
> be added later. Even if we added private cases, you wouldn’t need to mark 
> those enums as specially exhaustive or not; that would be implied. It’s an 
> accommodation for things which don’t exist yet, so 

Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-21 Thread Jordan Rose via swift-evolution
Thanks for your response, Ash. Comments inline.


> On Dec 20, 2017, at 11:49, Ash Furrow via swift-evolution 
>  wrote:
> 
> Proposal link: 
> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>  
> 
> 
> What is your evaluation of the proposal?
> 
> -1.
> 
> Is the problem being addressed significant enough to warrant a change to 
> Swift?
> 
> I’m afraid not.
> 
> From my perspective as a Swift user, this change represents nontrivial 
> language churn without providing a solution to a problem I have. The proposal 
> doesn’t describe any benefits to me as an open source library maintainer or 
> as a Swift developer. With earnest respect, the motivation section reads like 
> “enums grow sometimes, but we like to exhaustively switch over them, so 
> wouldn’t it be cool if …”, which is only a theoretical motivation. It fails 
> to describe how and why this proposal would improve my Swift code or my 
> experience using Swift.
> 
> This appears to be a solution to a non-existing problem. I worry that making 
> this change will alienate developers from Swift and I caution against 
> accepting it.
> 
I wish it were, but unfortunately it's a very real problem. Cases are added to 
Cocoa all the time, and currently it is undefined behavior if one of those 
cases makes it into a switch defined in Swift. ("undefined behavior" = "no 
guarantees of memory safety, type safety, or even which functions are going to 
get invoked in the rest of the function") This is a terrible state of affairs 
that we need to do something about, and "make it a deterministic trap" isn't a 
good enough answer.

We also already know that we have clients that want to add new cases to Swift 
enums without breaking source or binary compatibility: the Apple overlays. It's 
true that this is more of a "theoretical motivation" for source framework 
authors at this time.



> Does this proposal fit well with the feel and direction of Swift?
> 
> It may have at one time point in time, but not now.
> 
> The chaotic churn of the language, the syntax, and the standard library is 
> supposed to be behind us. We need to accept that things fell into place as 
> they did, often in imperfect ways. We probably could correct all the 
> imperfections, but when would we ever stop? Language churn has a cost. This 
> proposal is something that I could definitely see being a part of Swift 2 or 
> Swift 3, but we have already decided that enums are exhaustive. This change, 
> and changes fundamental to the cognitive model Swift programmers already have 
> of their tool, need to be heavily weighted against language churn

There's a difference between imperfections that mean something is a bit harder 
to use, or not named the right thing, and imperfections that lead to crashes or 
core capabilities not being expressible. Being able to add cases to enums is 
something Cocoa developers have relied on for years in Objective-C, and so it 
isn't really sensible to not have this feature in Swift. I definitely wish we 
could have gotten to it sooner.


> 
> If you have used other languages or libraries with a similar feature, how do 
> you feel that this proposal compares to those?
> 
> Scala’s match syntax bears a striking resemblance to Swift’s switch syntax; 
> however, Scala does not require exhaustive cases. If the developer does not 
> include a default case and none of the cases match the expression, an 
> exception is thrown. Because of Swift’s error-handling model, I don’t know 
> that this behaviour would be desirable either (though I will say it makes 
> sense in Scala).

This is certainly an option; it's in the proposal under "Alternatives 
considered" as 'switch!' (causing a deterministic trap rather than an 
exception). That doesn't seem sufficient to deal with the realities of Cocoa, 
though.

Jordan


> 
> How much effort did you put into your review? A glance, a quick reading, or 
> an in-depth study?
> 
> I read most of the proposal (okay I skimmed some of the nitty-gritty, but I 
> read to the end of “Source compatibility”) as well as looked over the 
> pre-review threads and skimmed GitHub pull request thread.
> 
> -- 
> Ash Furrow
> https://ashfurrow.com/ 
> On December 19, 2017 at 5:58:14 PM, Ted Kremenek via swift-evolution 
> (swift-evolution@swift.org ) wrote:
> 
>> The review of "SE 0192 - Non-Exhaustive Enums" begins now and runs through 
>> January 3, 2018.
>> 
>> The proposal is available here:
>> 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>>  
>> 
>> Reviews are an important part of the Swift evolution process. All review 
>> feedback should be sent to the swift-evolution mailing 

Re: [swift-evolution] The Non-Exhaustive Enums proposal kills one of Swift's top features - change proposal

2017-12-21 Thread Jordan Rose via swift-evolution
[Proposal: 
https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md]

> On Dec 21, 2017, at 09:49, Ignacio Soto via swift-evolution 
>  wrote:
> 
> I think I speak for the entire Swift community when I say that Swift's enums, 
> together with the ability to switch over them exhaustively and having 
> compile-time guarantees, is one of the best features in Swift. I'm regularly 
> annoyed by this when writing other languages like Java and JS (default: throw 
> new IllegalArgumentException();)
> 
> Now, that's not to say I don't understand why this proposal is necessary. I 
> totally get it, and the existing decisions make a lot of sense to me. But I'd 
> like us to introduce this while maintaining the ability to guarantee 
> exhaustive switch statements, no matter how the enum was defined.
> 
> Example: imagine a theoretical SampleKit defines:
> public enum E {
> case A
> case B
> }
> 
> It's implicitly non-exhaustive, possibly because the author was not aware of 
> the default (which would likely happen often), or possibly because they made 
> a conscious decision, as they expect to expand the cases in the future.
> 
> In my app, I use SampleKit and decide that I want to make sure I handle all 
> cases:
> 
> switch e {
> case A: break
> case B: break
> default: break // This becomes necessary
> }
> 
> As the proposal stands right now, I'm forced to handle any future cases. 
> That's fine. What's not fine in my opinion, is that in doing so I lose the 
> ability to keep this exhaustiveness moving forward. If I update SampleKit to 
> v2.0, I want to know at compile-time if there are new cases I need to be 
> aware of (instead of going to some generic handling path). Instead, I’m left 
> in the same place I would in other languages like Java or JS:
> 
> // No error :(
> switch e {
> case A: break
> case B: break
> default: break
> }
> 
> Proposed Solution
> 
> What I’m proposing is that we introduce a new keyword, unknown (or a better 
> name), that serves as a way to handle cases that aren’t yet known, but not 
> those that are.
> 
> // Error: missing case C
> switch e {
> case A: break
> case B: break
> unknown: break // Would handle future cases
> }
> 
> With this, you shouldn’t be able to use default AND unknown at the same time, 
> as default implicitly includes unknown.
> 
> Thanks for reading, and I hope you can consider this change (or some 
> variation of it).

Hi, Nacho. This is discussed in the proposal as "'future' cases" under 
"Alternatives considered". The main blocker was that such a case becomes 
untestable (see also "Testing invalid cases"). That didn't seem like an 
acceptable state of affairs to me or to the people I had originally discussed 
the proposal with, but maybe the community feels differently?

I would love if someone could think of something I haven't yet; by no means do 
I think I'm the only one who can have ideas in this space.

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


Re: [swift-evolution] [swift-evolution-announce] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-20 Thread Jordan Rose via swift-evolution


> On Dec 19, 2017, at 20:15, Howard Lovatt via swift-evolution 
>  wrote:
> 
> As an aside: there seems to be increasingly comments about proposals that say:
> 
>   1. This was discussed at the evaluation stage and rejected. 
>   2. This is how it is implemented in the patch.
> 
> And other comments along those lines. Neither the pre-proposal discussions 
> nor the proposed implementation are intended to limit the scope of the 
> review. Therefore I don’t think people should raise this as reasons. You 
> should remember that the process is deliberately staged this way and 
> different people may well be commenting (in fact the process rather assumes 
> that people in the formal review will be a wider set of people). 
> 
> Anyway gripe over. 
> 
> Proposal link: 
> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>  
> 
> What is your evaluation of the proposal ?
> 
> +1/2
> 
> I only give this a half because whilst it is important I can see three issues:
> 
>   1. It doesn’t seem very Swift like to have a different rule, default 
> non-exhaustive, for public as opposed to non-public. 
> 
I guess people are still misunderstanding this. Within a module, switches must 
still be exhaustive, always, because the switch can never get out of sync with 
the enum definition. The "exhaustive" annotation only applies to clients across 
module boundaries.


>   2. It doesn’t seem very Swift like to have the default the unsafe case. 
> 
I find it interesting that you call this the "unsafe" case. From my point of 
view , it is very much in line with Swift's current 
design to have the default be "force clients to consider all possibilities" 
(like Optional), as well as "let library authors decide what a client should be 
able to rely on" (like 'open').


>   3. Other languages have better solutions - see below under other languages
> 
> Is the problem being addressed significant enough to warrant a change to 
> Swift?
> 
> Yes, Swift ABI compatibility going forwards is important
> 
> Does this proposal fit well with the feel and direction of Swift?
> 
> No. As mentioned above different rules for public and a non-safe default 
> don’t see that Swift like. 
> 
> If you have used other languages or libraries with a similar feature, how do 
> you feel that this proposal compares to those?
> 
> Both Java and Scala have a better solution. In these languages enums (Scala 
> calls them case classes) can implement protocols and the user of an enum 
> rarely writes a switch statement, instead they call protocol methods. Enums 
> in these languages are a fixed set of derived classes; i.e. normal OO 
> programming rather than functional programming, which works well in the case 
> of wanting to expand later the number of enum cases. 
> 
This is an interesting point that I probably should have expanded more in the 
"comparison with other languages" section. Swift has protocols too, and you can 
build most of the features of enums with protocols. (Set aside the inferior 
pattern-matching for now; that's something we could add to Swift.) This is 
currently much more heavyweight than enums, but let's roll with it.

Next we have to deal with C enums, which can have "private cases" or 
newly-added cases. Okay, maybe those get imported as structs instead, like 
NS_OPTIONS. That's not too bad, is it?

Now we're back in a place where 'enum' always means "exhaustive". That's good, 
right? Except…now we have a kind of type where the recommendation will be 
"don't use these in your library's public API; they'll be stuck that way 
forever", a trap waiting for library authors. That's a problem C has with 
structs, and it's something we don't want to bring into Swift. Making a library 
necessarily requires more care than making an app, but we don't want there to 
be techniques that only make sense within a module, and are discouraged when 
writing a library.

So we can implement this world in Swift. I just don't think it'll be a better 
one. When someone makes a library, the default should be safe for them. That 
means that they reserve the ability to add cases, and also to make the enum 
"exhaustive" in the future if it turns out that's the right thing to do.

(You're free to continue to disagree with me on this; thanks for getting me to 
write it out.)

Jordan


> How much effort did you put into your review? A glance, a quick reading, or 
> an in-depth study?
> 
> Have followed the discussions. Used enums in Swift and other languages 
> extensively. 
> 
> -- Howard.
> 
> On 19 Dec 2017, at 12:58 pm, Ted Kremenek  > wrote:
> 
>> When replying, please try to keep the proposal link at the top of the 
>> message:
>> 
>> Proposal link: 
>> 

Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-20 Thread Jordan Rose via swift-evolution
Thanks for the feedback, Dave! Responses inline.

> On Dec 20, 2017, at 09:23, Dave DeLong via swift-evolution 
>  wrote:
> 
> 
> 
>> On Dec 19, 2017, at 3:58 PM, Ted Kremenek via swift-evolution 
>> > wrote:
>> 
>> The review of "SE 0192 - Non-Exhaustive Enums" begins now and runs through 
>> January 3, 2018.
>> 
>> The proposal is available here:
>> 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>>  
>> 
>> When reviewing a proposal, here are some questions to consider:
>> 
>> What is your evaluation of the proposal?
>> 
> A very strong -1. I do not believe this is the appropriate solution to the 
> problem.
> 
> • While the goal of the proposal is to ensure the correctness of *client* 
> code, it does nothing to enforce the correctness of evolving *library* code. 
> As a library author, I can declare a public enum as “exhaustive”, yet still 
> add a new case in the next release. Nothing in the proposal prevents me from 
> doing this, yet doing so would obviously break any clients of my library. 

There are some ideas listed to prevent this occurence in the proposal. 
Additionally, such a change may be desirable for a major version release of a 
library distributed as source. Nothing prevents someone from changing the type 
of an 'open' method either, but that can also be a breaking change.


> • The name “exhaustive” is misleading for uninformed library authors. An 
> author creates an enum and then thinks “is that all of the cases? Yep! OK, 
> it’s @exhaustive”. Then the next evolution of the library occurs, new cases 
> arise, and now the enum isn’t exhaustive to handle the new cases. So a case 
> gets added, and the formerly-but-not-actually-exhaustive enum is re-stamped 
> as exhaustive, because it once again handles all known cases. “Exhaustive” is 
> not a strong enough name. It does not contain the idea of eternal permanence. 
> Once an enum gets branded as exhaustive and shipped as such, *it can never 
> change*. “Exhaustive” does not imply that, and the lack of that implication 
> will confuse library authors.

I don't think anyone loves the current name, but we haven't come up with a 
better one (cf. "Alternatives considered"). Any suggestions?

(As mentioned above, I also don't think this is an impossible situation either. 
But we'd certainly want people to not do it by accident.)



> • This proposal does not address the case of “publicly exhaustive enums with 
> private cases”. Consider NSExpression.ExpressionType: when creating 
> NSPredicates from format strings, it is easy to create sub-expressions whose 
> expression types are not one of the publicly listed cases. Based on the 
> proposal, NSExpression.ExpressionType would be correctly imported as a 
> non-exhaustive enum. HOWEVER. There is nothing *stopping* a library author 
> from declaring a publicly exhaustive enum (like NSExpression.ExpressionType), 
> but having private cases that get leaked (accidentally or not) past the 
> public barrier and end up in client code. This proposal does nothing to 
> prevent that.

Swift does not currently support enums with private cases (as mentioned in the 
proposal), but if it did, such an enum could not be marked "exhaustive". On the 
C side, there's no way to check that no one's secretly using a private case, so 
we have to trust the author of the type to use the equivalent annotation 
responsibly.


> The summary of these objections is this: you fundamentally cannot trust 
> libraries that are not bundled with your application to not change in 
> unexpected ways. Or in other words, if you don’t have the source code, you 
> cannot trust it. And even if you do have the source code, it’s still 
> questionable once you start bridging things in from other languages where 
> this sort of safety is not enforced.

This is fair, and we should consider leaving an implicit "default: 
fatalError()" in even for supposedly exhaustive enums (at least those imported 
from C).

Jordan


> 
> To summarize the summary: Leaving a judgement of “exhaustive or not” up to 
> fallible library authors isn’t safe.
> 
> To summarize the summary of the summary: people are a problem.
>> Is the problem being addressed significant enough to warrant a change to 
>> Swift?
>> 
> Yes, the problem is significant, but in my opinion this is the wrong answer.
>> Does this proposal fit well with the feel and direction of Swift?
>> 
> No. Implementing this proposal would give the appearance of safety while 
> still leaving developers subtly but dangerously vulnerable to imperfectly 
> written libraries (ie, all of them).
>> How much effort did you put into your review? A glance, a quick reading, or 
>> an in-depth study?
>> 
> I’ve been following the email threads, and I’ve spent years as a library 
> 

Re: [swift-evolution] [swift-evolution-announce] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-20 Thread Jordan Rose via swift-evolution
Thanks for your feedback! I think you're missing the "libraries in the OS" 
aspect of this: if there are Swift libraries in iOS, and Apple ships a software 
update, some enums will get new cases. That means you can always end up in a 
default case if you were switching over those enums, and that's why 'switch!' 
has a '!' in it.

I sympathize with the "exhaustiveness diagnostics must be preserved" idea, but 
it simply doesn't account for package-based development. If libraries are 
allowed to add new cases without breaking source stability, they must not 
introduce new errors into clients. An app author cares about that because they 
want to use the latest Apple SDKs right away, even before any of their 
third-party library dependencies have released an update.

I do actually expect a number of third-party libraries to both mark their enums 
as exhaustive and add new cases in major releases (the ones that are allowed to 
be source-breaking). It's also a source-compatible change to mark a public enum 
as "exhaustive" that wasn't before, so I also expect an early flurry of bug 
reports when people realize this is important in Swift 5. But I think people 
will get the hang of it.

To be clear, I'm open to ideas in this space. It's just that both of the ideas 
in "Alternatives Considered" feel incomplete to me, and unfortunately we do 
have to do something in the near term for both imported enums and for Apple 
APIs (the overlays).

Jordan


> On Dec 20, 2017, at 01:44, Jérôme Duquennoy via swift-evolution 
>  wrote:
> 
> Hi guys,
> 
> I am part of the people mentioned in "Preserve exhaustiveness diagnostics for 
> non-exhaustive enums" : I see the completeness check as a major feature of 
> enums, that makes a difference between it and a list of constants in multiple 
> cases.
> So much that in the coding rules of the company I work for, the use of 
> default case requires some specific justification at review time.
> 
> I have a positive opinion on this evolution if it comes with a solution to 
> preserve exhaustiveness diagnostics. Otherwise, I think it lowers the ease of 
> maintenance of code using external libraries.
> 
> One of the risk I fear is that libraries builders might just forget to add 
> the @exhaustive keyword. So it might end up being a pretty common case to 
> have to specify "I want this switch to be exhaustive", just to not loose on 
> maintainability. This could lead to a situation similar to the "fileprivate", 
> that ended up being used much more than expected.
> 
> It is not exactly in the scope of that review, but in the two solution for 
> enforcing exhaustiveness drafted in that proposal, I would rather avoid the 
> second one (the switch!). It would contradict the fact that as of today, the 
> use of the exclamation mark in swift is a pretty clear sign that you are 
> disabling some compiler-provided security at your own risk, and that it might 
> lead to runtime crash (force unwrap, force cast, ...).
> Here, requesting a switch to be exhaustive is adding one more compiler check. 
> It cannot lead to a runtime crash.
> I will be happy to discuss it further in a future review.
> 
> Jerome
> ___
> 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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-20 Thread Jordan Rose via swift-evolution


> On Dec 20, 2017, at 05:36, Karl Wagner via swift-evolution 
>  wrote:
> 
> 
> 
>> On 19. Dec 2017, at 23:58, Ted Kremenek via swift-evolution 
>> > wrote:
>> 
>> The review of "SE 0192 - Non-Exhaustive Enums" begins now and runs through 
>> January 3, 2018.
>> 
>> The proposal is available here:
>> 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>>  
>> +1,
>>  it needs to happen (and ASAP, since it _will_ introduce source-breaking 
>> changes one way or the other).
> 
> I think non-exhaustive is the correct default. However, does this not mean 
> that, by default, enums will be boxed because the receiver doesn’t know their 
> potential size?

It's not always boxing, but yes, there will be more indirection if the compiler 
can't see the contents of the enum. (More on that below.)


> That would mean that the best transition path for multi-module Apps would be 
> to make your enums @exhaustive, rather than adding “default” statements 
> (which is unfortunate, because I imagine when this change hits, the way 
> you’ll notice will be complaints about missing “default” statements).

Yep, that's going to be the recommendation. The current minimal-for-review 
implementation does not do this but I'd like to figure out how to improve that; 
at the very least it might be a sensible thing to do in the migrator.

> 
> I do have some thoughts about how we could ease the transition (for this and 
> other resilience-related changes), but it’s best to leave that to a separate 
> discussion.
> 
> The one thing I’m still not overly fond of is the name - I would like us to 
> keep the set of resilience/optimisation related keywords to a minimum. 
> “exhaustive” for enums feels an awful lot like “fixed_contents” for structs - 
> couldn’t we come up with a single name which could be used for both? I don’t 
> think anybody’s going to want to use “exhaustive” for structs.

The core team was very focused on this too, but I contend that "exhaustive" is 
not about optimization and really isn't even about "resilience" (i.e. the 
ability to evolve a library's API while preserving binary compatibility). It's 
a semantic feature of an enum, much like 'open' or 'final' is for classes, and 
it affects what a client can or can't do with an enum. For libaries compiled 
from source, it won't affect performance at all—the compiler still knows the 
full set of cases in the current version of the library even if the programmer 
is forced to consider future versions.

I'm working on the fixed-contents proposal now, though it won't be ready for a 
while, and the same thing applies there: for structs compiled from source, the 
compiler can still do all the same optimizations. It's only when the library 
has binary compatibility concerns that we need to use extra indirection, and 
then "fixed-contents" becomes important. (As currently designed, it doesn't 
affect what clients can do with the struct at all.) This means that I don't 
expect a "normal" package author to write "fixed-contents" at all (however it 
ends up being spelled), whereas "exhaustive" is a fairly normal thing to 
consider whenever you make an enum public.

I hope that convinces you that "fixed-contents" and "exhaustive" don't need to 
have the same name. I don't think anyone loves the particular name 
"exhaustive", but as you see in the "Alternatives considered" we didn't manage 
to come up with anything significantly better. If reviewers all prefer 
something else we'd consider changing it.

Thanks for responding!
Jordan

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


Re: [swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

2017-12-20 Thread Jordan Rose via swift-evolution
Thanks for the links, Colin. I think neither of these approaches are 
appropriate for Swift, largely for the same reason: they can't be used to 
define a library's API. Polymorphic variants are ad-hoc union types (much like 
tuples are ad-hoc structs) which—apart from having other problems in Swift 
previously discussed on this list—means that you can't add new cases to them. 
That is, any API which takes `(red(r: Int) | green(g: Int) | blue(b: Int))` 
today can't add `alpha(a: Int)` in the future, because that would change the 
type. ML-style exceptions have the opposite problem; they are completely 
unconstrained and so a client can add new "cases" without the library author 
being prepared. (Swift's error-handling model in general behaves a lot like 
this, except it doesn't get ML's knowledge of which errors might be thrown.)

I'd sum this up by saying Swift is differing from ML and from most other 
languages because it is solving a different problem. Swift is designed such 
that the compiler does not require whole-program knowledge to produce a 
correct, working, type-safe program. It will use any cross-module knowledge it 
can for optimization purposes, but the language semantics do not depend on it. 
And this is critical, as you note, for libraries with binary compatibility 
concerns.

Jordan


> On Dec 20, 2017, at 10:13, Colin Barrett via swift-evolution 
>  wrote:
> 
> I very much agree with the motivations for this proposal. It identifies a 
> clear, urgent need.
> 
> I disagree that the proposed solution is a good solution. It makes a very 
> significant, and confusing, change to the language that does not have much 
> precedent in other languages. This goes against the stated design guidelines 
> for the Swift language.
> 
> I would much rather see Swift follow the lead of other ML languages and 
> introduce something like “polymorphic variants”[1] or Standard ML’s 
> exceptions (which are "open” in this way by default)
> 
> Thanks for everyone's hard work, particularly the authors,
> -Colin
> 
> [1]: https://realworldocaml.org/v1/en/html/variants.html#polymorphic-variants 
> 
> [2]: https://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm 
>  
> 
>> On Dec 19, 2017, at 5:58 PM, Ted Kremenek via swift-evolution 
>> > wrote:
>> 
>> The review of "SE 0192 - Non-Exhaustive Enums" begins now and runs through 
>> January 3, 2018.
>> 
>> The proposal is available here:
>> 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>>  
>> 
>> Reviews are an important part of the Swift evolution process. All review 
>> feedback should be sent to the swift-evolution mailing list at:
>> 
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> 
>> or, if you would like to keep your feedback private, directly to the review 
>> manager. 
>> 
>> When replying, please try to keep the proposal link at the top of the 
>> message:
>> 
>> Proposal link: 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
>>  
>> 
>> ...
>> Reply text
>> ...
>> Other replies
>> What goes into a review of a proposal?
>> 
>> The goal of the review process is to improve the proposal under review 
>> through constructive criticism and, eventually, determine the direction of 
>> Swift. 
>> 
>> When reviewing a proposal, here are some questions to consider:
>> 
>> What is your evaluation of the proposal?
>> 
>> Is the problem being addressed significant enough to warrant a change to 
>> Swift?
>> 
>> Does this proposal fit well with the feel and direction of Swift?
>> 
>> If you have used other languages or libraries with a similar feature, how do 
>> you feel that this proposal compares to those?
>> 
>> How much effort did you put into your review? A glance, a quick reading, or 
>> an in-depth study?
>> 
>> Thanks,
>> Ted Kremenek
>> Review Manager
>> ___
>> 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

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


Re: [swift-evolution] [swift-evolution-announce] [Review] SE-0189: Restrict Cross-module Struct Initializers

2017-11-17 Thread Jordan Rose via swift-evolution


> On Nov 17, 2017, at 15:20, Ben Langmuir  wrote:
> 
> Hi Jordan,
> 
> First off, this is clearly the model we should have had all along ;-)  That 
> said, I have a concern about source-compat and our ability to automatically 
> migrate code impacted by this change.
>> Source compatibility
>> 
>> This makes existing code invalid in Swift 5, which is a source compatibility 
>> break.
>> 
> It's not just a source compatibility break, it's a break that cannot 
> necessarily be fixed if you don't control the module that vended the struct.  
> If a library doesn't expose an appropriate initializer, there is no way for 
> the client to invent one.  Similarly, this isn't going to be very amenable to 
> automatic migration, since
> a) there may not be a memberwise initializer to use
> b) even if there is, it may change the semantics to use it
> 
> There are two classes that we could theoretically migrate automatically:
> 1) C structs, since we know the initializer and its semantics
> 2) when we are migrating the code that defines the struct at the same time
> 
> The latter case might be tricky though, since it requires more global 
> knowledge than we have in today's migrator.
> 
> Any thoughts?  Do we have an idea how common this is or what kinds of places 
> it comes up in most often (in a single codebase with multiple modules vs 
> external dependencies vs C structs vs )?

This is good to bring up, but I think "this can't be migrated" is the correct 
answer for Swift structs. It's equivalent in my mind to when someone was 
passing 'nil' to something that wasn't annotated for nullability and now is 
marked '_Nonnull': it's source-breaking, and what you had before might even 
have worked, but there's no guarantee that it would keep working in the future. 
That's harder to sell for multi-module projects or even test targets, though. I 
don't have a great answer there, but I don't think it's worth bending over 
backwards to handle the "I migrate everything at once" case.

The C case is a bit harder to sell, which is why I made sure there were fix-its 
to suggest inserting `self.init()` (the zeroing initializer) in most cases 
(both in Swift 4 mode and Swift 5 mode). Not all C structs have that 
initializer (specifically, if they have a member marked _Nonnull), but nearly 
all do, and that's something the migrator could insert fairly easily, as you 
note.

The mitigating factor I'm hoping for is that these become warnings in Swift 
4.1, which is planned to ship in a mid-year Xcode (can I admit that publicly, 
swift-evolution?), and that therefore many people will have cleaned up their 
code well before they even think of switching to Swift 5. But yes, this is a 
breaking, non-migratable language change that's only strictly necessary for 
libraries with binary compatibility, which most libraries today are not, and 
that has to be acknowledged.

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


Re: [swift-evolution] [Review] SE-0189: Restrict Cross-module Struct Initializers

2017-11-14 Thread Jordan Rose via swift-evolution
Hi, David. This only affects cross-module use cases, which means that the 
automatically synthesized initializer doesn’t come into play (because it’s not 
public). Is the clarification you’re looking for something like “a 
'source-breaking change’ is something that can cause previously compiling code 
in another module to result in compile-time errors”?

Thanks for pointing out the potential for confusion here!
Jordan


> On Nov 14, 2017, at 12:55, David Hart  wrote:
> 
> I was initially quite confused about the proposal's first sentence: "Adding a 
> property to a public struct in Swift ought to not be a source-breaking 
> change.” Because I nearly always rely (like many developers) on struct 
> automatic initializers, adding a property is pretty much always a 
> source-breaking if I don’t write an explicit initializer with the same 
> signature as the old automatic. Can something be done to clarify the proposal 
> in that regard or is it too late?
> 
> David.
> 
>> On 14 Nov 2017, at 20:31, Ted Kremenek via swift-evolution 
>> > wrote:
>> 
>> The review of "SE-0189: Restrict Cross-module Struct Initializers" begins 
>> now and runs through November 21, 2017.
>> 
>> The proposal is available here:
>> 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0189-restrict-cross-module-struct-initializers.md
>>  
>> 
>> Reviews are an important part of the Swift evolution process. All review 
>> feedback should be sent to the swift-evolution mailing list at:
>> 
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> 
>> or, if you would like to keep your feedback private, directly to the review 
>> manager. 
>> 
>> When replying, please try to keep the proposal link at the top of the 
>> message:
>> 
>> Proposal link: 
>> https://github.com/apple/swift-evolution/blob/master/proposals/0189-restrict-cross-module-struct-initializers.md
>>  
>> 
>> ...
>> Reply text
>> ...
>> Other replies
>> What goes into a review of a proposal?
>> 
>> The goal of the review process is to improve the proposal under review 
>> through constructive criticism and, eventually, determine the direction of 
>> Swift. 
>> 
>> When reviewing a proposal, here are some questions to consider:
>> 
>> What is your evaluation of the proposal?
>> 
>> Is the problem being addressed significant enough to warrant a change to 
>> Swift?
>> 
>> Does this proposal fit well with the feel and direction of Swift?
>> 
>> If you have used other languages or libraries with a similar feature, how do 
>> you feel that this proposal compares to those?
>> 
>> How much effort did you put into your review? A glance, a quick reading, or 
>> an in-depth study?
>> 
>> Thanks,
>> Ted Kremenek
>> Review Manager
>> ___
>> 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


Re: [swift-evolution] [swift-evolution-announce] [Review] SE-0189: Restrict Cross-module Struct Initializers

2017-11-14 Thread Jordan Rose via swift-evolution
Hi, everyone, proposal author here. Caveat: I'll be on vacation for the last 
few days of this review period, so I won't see your comments after Saturday the 
18th (and possibly not even then, if you send them too late in the day). This 
is fine, of course, but if you have questions for me I may not be able to 
answer them until the following Monday, after the review has formally ended. 
Apologies if this ends up affecting you.

Thank you for your consideration and feedback!
Jordan

P.S. The implementation PR includes some changes to the Apple SDK overlays that 
demonstrate what's being proposed for imported C structs. I encourage you to 
take a look.


> On Nov 14, 2017, at 11:31, Ted Kremenek  wrote:
> 
> The review of "SE-0189: Restrict Cross-module Struct Initializers" begins now 
> and runs through November 21, 2017.
> 
> The proposal is available here:
> 
> https://github.com/apple/swift-evolution/blob/master/proposals/0189-restrict-cross-module-struct-initializers.md
>  
> 
> Reviews are an important part of the Swift evolution process. All review 
> feedback should be sent to the swift-evolution mailing list at:
> 
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> 
> or, if you would like to keep your feedback private, directly to the review 
> manager. 
> 
> When replying, please try to keep the proposal link at the top of the message:
> 
> Proposal link: 
> https://github.com/apple/swift-evolution/blob/master/proposals/0189-restrict-cross-module-struct-initializers.md
>  
> 
> ...
> Reply text
> ...
> Other replies
> What goes into a review of a proposal?
> 
> The goal of the review process is to improve the proposal under review 
> through constructive criticism and, eventually, determine the direction of 
> Swift. 
> 
> When reviewing a proposal, here are some questions to consider:
> 
> What is your evaluation of the proposal?
> 
> Is the problem being addressed significant enough to warrant a change to 
> Swift?
> 
> Does this proposal fit well with the feel and direction of Swift?
> 
> If you have used other languages or libraries with a similar feature, how do 
> you feel that this proposal compares to those?
> 
> How much effort did you put into your review? A glance, a quick reading, or 
> an in-depth study?
> 
> Thanks,
> Ted Kremenek
> Review Manager
> ___
> swift-evolution-announce mailing list
> swift-evolution-annou...@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution-announce

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


Re: [swift-evolution] Pitch: Restrict Cross-module Struct Initializers

2017-10-09 Thread Jordan Rose via swift-evolution


> On Oct 7, 2017, at 14:44, Chris Lattner <clatt...@nondot.org> wrote:
> 
> 
>> On Oct 6, 2017, at 2:32 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> While working on the non-exhaustive enums proposal I had it pointed out to 
>> me that structs actually currently leak implementation details across module 
>> boundaries, specifically their full set of stored properties. This only 
>> comes up in one place: when making an initializer for the struct in an 
>> extension in another module. We really want people to be able to change the 
>> stored properties in their structs between releases without it being a 
>> source break—that's half the point of computed properties—and it's also 
>> important for a struct author to be able to enforce invariants across the 
>> struct's properties. So after talking to a few other Apple Swift folks I put 
>> together this proposal:
>> 
>> https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md
>>  
>> <https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md>
>> 
>> This one's way smaller than the enum one, and hopefully fairly 
>> uncontroversial. Feedback welcome!
> 
> Great catch, +1 to the proposal!
> 
> Please add the point Xiodi mentions to the writing though so that the review 
> cycle adequately discusses it.  A "fragile struct” (for some definition of 
> fragility), but definitely including C structs, will have knowable stored 
> properties, and it isn’t clear that these should be subjected to this 
> restriction.  The win of the restriction in that case is the invariant point 
> you’re making.  It is best to address this head-on in the writing, and 
> mention the alternate approach in the ‘alternatives considered’ section 
> (along with why you think you’ve picked the right choice).

Good call. Updated the Alternatives Considered section with my response to 
Xiaodi, plus a specific section on C structs. (Also added the @testable part 
that Rudolf mentioned.)

Initial implementation has also been posted.

Jordan

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


Re: [swift-evolution] Property Getter Return Statement

2017-10-09 Thread Jordan Rose via swift-evolution


> On Oct 8, 2017, at 21:56, Slava Pestov via swift-evolution 
>  wrote:
> 
> 
> 
>> On Oct 7, 2017, at 7:07 AM, James Valaitis via swift-evolution 
>>  wrote:
>> 
>> Is it widely agreed that it is necessary to require a return statement on a 
>> one line property getter?
>> 
>> var session: AVCaptureSession { get { return layer.session } }
>> 
>> Or could we follow the convention for any other close and get rid of it? For 
>> me it seems redundant; the word `get` literally precedes the closure.
> 
> In multi-file projects, re-compiling one file that references the property 
> would necessitate type checking the body of the getter, even if the getter is 
> defined in a different source file. So one reason not to have this would be 
> to avoid slowing down type checking.

This is not correct. Omitting the "return" is different from omitting the 
property's type.

(I'm minorly in favor of allowing the 'return' to be omitted for 
single-expression getters. Not enough to be the person who implements it, but 
enough to +1 a proposal-with-implementation even in the Swift 5 timeframe.)

Jordan

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


Re: [swift-evolution] Pitch: Restrict Cross-module Struct Initializers

2017-10-09 Thread Jordan Rose via swift-evolution
Thanks for bringing this up. I thought of this only after posting the proposal, 
and I think the answer ought to be "you're exempt from this restriction if you 
use `@testable import`". I still want to work out that this is implementable 
even for future frameworks with binary compatibility concerns (though the 
testable-only parts don't have to be a binary-compatible interface), but you're 
right that the proposal isn't really complete without that exception. I'll add 
it today.

Jordan


> On Oct 8, 2017, at 12:49, Rudolf Adamkovič via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> This is a great proposal.
> 
> However, it can make unit testing problematic. Let me explain.
> 
> In my unit tests, I often add initializers that set stored properties 
> directly to create structs with desired data and those are then fed into the 
> struct that’s under test. This is to test in complete isolation, without 
> invoking the “production” initializer of structs that aren’t under test. In 
> other words, I want to avoid invoking any behavior of structs that aren’t 
> directly under test.
> 
> Would the rules described in this proposal be enforced also for testing 
> targets/modules?
> 
> R+
> 
> Sent from my iPhone
> 
> On 7 Oct 2017, at 23:44, Chris Lattner via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> 
>> 
>>> On Oct 6, 2017, at 2:32 PM, Jordan Rose via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> While working on the non-exhaustive enums proposal I had it pointed out to 
>>> me that structs actually currently leak implementation details across 
>>> module boundaries, specifically their full set of stored properties. This 
>>> only comes up in one place: when making an initializer for the struct in an 
>>> extension in another module. We really want people to be able to change the 
>>> stored properties in their structs between releases without it being a 
>>> source break—that's half the point of computed properties—and it's also 
>>> important for a struct author to be able to enforce invariants across the 
>>> struct's properties. So after talking to a few other Apple Swift folks I 
>>> put together this proposal:
>>> 
>>> https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md
>>>  
>>> <https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md>
>>> 
>>> This one's way smaller than the enum one, and hopefully fairly 
>>> uncontroversial. Feedback welcome!
>> 
>> Great catch, +1 to the proposal!
>> 
>> Please add the point Xiodi mentions to the writing though so that the review 
>> cycle adequately discusses it.  A "fragile struct” (for some definition of 
>> fragility), but definitely including C structs, will have knowable stored 
>> properties, and it isn’t clear that these should be subjected to this 
>> restriction.  The win of the restriction in that case is the invariant point 
>> you’re making.  It is best to address this head-on in the writing, and 
>> mention the alternate approach in the ‘alternatives considered’ section 
>> (along with why you think you’ve picked the right choice).
>> 
>> -Chris
>> 
>> 
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> ___
> 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


Re: [swift-evolution] Pitch: Restrict Cross-module Struct Initializers

2017-10-06 Thread Jordan Rose via swift-evolution
Oh, sorry, the missing piece of that is 'inlinable': an inlinable memberwise 
initializer in a fixed-layout struct should have no performance overhead in an 
optimized build.

Jordan


> On Oct 6, 2017, at 15:25, Jordan Rose <jordan_r...@apple.com> wrote:
> 
> Good question. Slava and I talked about that and decided that there wasn't 
> much point. '_fixed_layout' still doesn't cover the invariant problem, and 
> memberwise initializers are really common when that's not relevant. We don't 
> think there should ever be a reason to write _fixed_layout in a source 
> package, and we wouldn't want this to become one.
> 
> Jordan
> 
> 
>> On Oct 6, 2017, at 15:23, Xiaodi Wu <xiaodi...@gmail.com 
>> <mailto:xiaodi...@gmail.com>> wrote:
>> 
>> Presumably, @_fixed_layout and its future formalized cousin would restore 
>> the current functionality?
>> 
>> 
>> On Fri, Oct 6, 2017 at 16:32 Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> While working on the non-exhaustive enums proposal I had it pointed out to 
>> me that structs actually currently leak implementation details across module 
>> boundaries, specifically their full set of stored properties. This only 
>> comes up in one place: when making an initializer for the struct in an 
>> extension in another module. We really want people to be able to change the 
>> stored properties in their structs between releases without it being a 
>> source break—that's half the point of computed properties—and it's also 
>> important for a struct author to be able to enforce invariants across the 
>> struct's properties. So after talking to a few other Apple Swift folks I put 
>> together this proposal:
>> 
>> https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md
>>  
>> <https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md>
>> 
>> This one's way smaller than the enum one, and hopefully fairly 
>> uncontroversial. Feedback welcome!
>> 
>> Jordan
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 

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


Re: [swift-evolution] Pitch: Restrict Cross-module Struct Initializers

2017-10-06 Thread Jordan Rose via swift-evolution
Good question. Slava and I talked about that and decided that there wasn't much 
point. '_fixed_layout' still doesn't cover the invariant problem, and 
memberwise initializers are really common when that's not relevant. We don't 
think there should ever be a reason to write _fixed_layout in a source package, 
and we wouldn't want this to become one.

Jordan


> On Oct 6, 2017, at 15:23, Xiaodi Wu <xiaodi...@gmail.com> wrote:
> 
> Presumably, @_fixed_layout and its future formalized cousin would restore the 
> current functionality?
> 
> 
> On Fri, Oct 6, 2017 at 16:32 Jordan Rose via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> While working on the non-exhaustive enums proposal I had it pointed out to me 
> that structs actually currently leak implementation details across module 
> boundaries, specifically their full set of stored properties. This only comes 
> up in one place: when making an initializer for the struct in an extension in 
> another module. We really want people to be able to change the stored 
> properties in their structs between releases without it being a source 
> break—that's half the point of computed properties—and it's also important 
> for a struct author to be able to enforce invariants across the struct's 
> properties. So after talking to a few other Apple Swift folks I put together 
> this proposal:
> 
> https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md
>  
> <https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md>
> 
> This one's way smaller than the enum one, and hopefully fairly 
> uncontroversial. Feedback welcome!
> 
> Jordan
> ___
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>

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


[swift-evolution] Pitch: Restrict Cross-module Struct Initializers

2017-10-06 Thread Jordan Rose via swift-evolution
While working on the non-exhaustive enums proposal I had it pointed out to me 
that structs actually currently leak implementation details across module 
boundaries, specifically their full set of stored properties. This only comes 
up in one place: when making an initializer for the struct in an extension in 
another module. We really want people to be able to change the stored 
properties in their structs between releases without it being a source 
break—that's half the point of computed properties—and it's also important for 
a struct author to be able to enforce invariants across the struct's 
properties. So after talking to a few other Apple Swift folks I put together 
this proposal:

https://github.com/jrose-apple/swift-evolution/blob/restrict-cross-module-struct-initializers/proposals/-restrict-cross-module-struct-initializers.md
 


This one's way smaller than the enum one, and hopefully fairly uncontroversial. 
Feedback welcome!

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


Re: [swift-evolution] A path forward on rationalizing unicode identifiers and operators

2017-10-03 Thread Jordan Rose via swift-evolution
>  - Imports are per-module, not per-source-file, so this couldn’t be used to 
> “user-partition” the identifier and operator space.  It could be a way to 
> make it clear that the user is opting into these explicitly.

Imports actually are per-source-file. At times they appear to be per-module 
because we don't check this properly for extensions, but those aren't strictly 
per-module either; you also have access to extensions in any module you've 
recursively imported. Which is terrible.

Jordan

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


Re: [swift-evolution] Fix "private extension" (was "Public Access Modifier Respected in Type Definition")

2017-10-02 Thread Jordan Rose via swift-evolution


> On Oct 2, 2017, at 18:06, Jose Cheyo Jimenez <ch...@masters3d.com> wrote:
> 
>> 
>> On Oct 2, 2017, at 5:33 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> 
>> 
>>> On Oct 2, 2017, at 03:25, Vladimir.S via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> On 01.10.2017 1:18, Chris Lattner wrote:
>>>>> On Sep 29, 2017, at 10:42 AM, Xiaodi Wu via swift-evolution 
>>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>>> 
>>>>> Vladimir, I agree with you on that change, but it’s a separate topic from 
>>>>> this one.
>>>>> 
>>>>> Tony is absolutely correct that this topic has already been discussed. It 
>>>>> is a deliberate design decision that public types do not automatically 
>>>>> expose members without explicit access modifiers; this has been brought 
>>>>> up on this list, and it is clearly not in scope for discussion as no new 
>>>>> insight can arise this late in the game. The inconsistency with public 
>>>>> extensions was brought up, the proposed solution was to remove modifiers 
>>>>> for extensions, but this proposal was rejected. So, the final design is 
>>>>> what we have.
>>>> Agreed.  The core team would only consider a refinement or change to 
>>>> access control if there were something actively broken that mattered for 
>>>> ABI stability.
>>> 
>>> So we have to live with *protected* extension inconsistency for very long 
>>> time just because core team don't want to even discuss _this particular_ 
>>> inconsistency(when access level in *private extension* must be private, not 
>>> fileprivate)?
>>> 
>>> Yes, we decided that access level for extension will mean a default and top 
>>> most access level for nested methods, OK. But even in this rule, which 
>>> already differ from access modifiers for types, we have another one special 
>>> case for 'private extension'.
>>> 
>>> Don't you think this is not normal situation and actually there IMO can't 
>>> be any reason to keep this bug-producing inconsistency in Swift? 
>>> (especially given Swift 5 seems like is a last moment to fix this)
>> 
>> I hate to say it but I'm inclined to agree with Vladimir on this. "private 
>> extension" has a useful meaning now distinct from "fileprivate extension", 
>> and it was an oversight that SE-0169 
>> <https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md>
>>  didn't include a fix here. On this very narrow, very specific access 
>> control issue I think it would still be worth discussing; like Xiaodi said 
>> it's not related to James' original thread-starter.
>> 
>> (I maintain that the current model does not include a special case; it 
>> simply means the 'private' is resolved at the level of the extension rather 
>> than the level of its members. But that isn't what people expect and it's 
>> not as useful.)
>> 
>> 
>> I agree that changing the behavior of all access modifiers on extensions is 
>> out of scope. (I also agree that it is a bad idea. Sorry, James, but wanting 
>> 'pubic' here indicates that your mental model of extensions does not match 
>> what Swift is actually doing, and that could get you into trouble.)
>> 
>> Jordan
> 
> 
> I am not in favor of including a special case for “private extension” because 
> like Jordan said, private is resolved at the level of the extension which is 
> always top level currently. A change would make sense if we start allowing 
> extensions that are not top level. Anything private that is top level is 
> equivalent to fileprivate so why should “private extension” be any different?

It's fair to be against this change, but I want to point out that neither of 
these are "special cases". Instead it's "access is resolved at the level of 
extension and then applied to the member" vs. "access is applied to the members 
and then resolved".

Jordan

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


Re: [swift-evolution] Fix "private extension" (was "Public Access Modifier Respected in Type Definition")

2017-10-02 Thread Jordan Rose via swift-evolution


> On Oct 2, 2017, at 03:25, Vladimir.S via swift-evolution 
>  wrote:
> 
> On 01.10.2017 1:18, Chris Lattner wrote:
>>> On Sep 29, 2017, at 10:42 AM, Xiaodi Wu via swift-evolution 
>>>  wrote:
>>> 
>>> Vladimir, I agree with you on that change, but it’s a separate topic from 
>>> this one.
>>> 
>>> Tony is absolutely correct that this topic has already been discussed. It 
>>> is a deliberate design decision that public types do not automatically 
>>> expose members without explicit access modifiers; this has been brought up 
>>> on this list, and it is clearly not in scope for discussion as no new 
>>> insight can arise this late in the game. The inconsistency with public 
>>> extensions was brought up, the proposed solution was to remove modifiers 
>>> for extensions, but this proposal was rejected. So, the final design is 
>>> what we have.
>> Agreed.  The core team would only consider a refinement or change to access 
>> control if there were something actively broken that mattered for ABI 
>> stability.
> 
> So we have to live with *protected* extension inconsistency for very long 
> time just because core team don't want to even discuss _this particular_ 
> inconsistency(when access level in *private extension* must be private, not 
> fileprivate)?
> 
> Yes, we decided that access level for extension will mean a default and top 
> most access level for nested methods, OK. But even in this rule, which 
> already differ from access modifiers for types, we have another one special 
> case for 'private extension'.
> 
> Don't you think this is not normal situation and actually there IMO can't be 
> any reason to keep this bug-producing inconsistency in Swift? (especially 
> given Swift 5 seems like is a last moment to fix this)

I hate to say it but I'm inclined to agree with Vladimir on this. "private 
extension" has a useful meaning now distinct from "fileprivate extension", and 
it was an oversight that SE-0169 

 didn't include a fix here. On this very narrow, very specific access control 
issue I think it would still be worth discussing; like Xiaodi said it's not 
related to James' original thread-starter.

(I maintain that the current model does not include a special case; it simply 
means the 'private' is resolved at the level of the extension rather than the 
level of its members. But that isn't what people expect and it's not as useful.)


I agree that changing the behavior of all access modifiers on extensions is out 
of scope. (I also agree that it is a bad idea. Sorry, James, but wanting 
'pubic' here indicates that your mental model of extensions does not match what 
Swift is actually doing, and that could get you into trouble.)

Jordan

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


Re: [swift-evolution] Pitch: Cross-module inlining and specialization

2017-10-02 Thread Jordan Rose via swift-evolution
Today's @available can't be the thing that makes symbols public, since it's 
also used to affect the availability context for private symbols. The one 
described in the library evolution document is specifically about marking 
something available with respect to the current module. So we would want 
another spelling.

(But it definitely shouldn't be "_versioned", which doesn't mean anything if we 
don't have "versions".)

Jordan


> On Oct 2, 2017, at 13:47, Slava Pestov via swift-evolution 
>  wrote:
> 
> Thanks for taking a look.
> 
> @_versioned is not something we want to keep in the long term. The original 
> idea was to allow @available annotations to be put on internal declarations, 
> which would have the effect of giving their symbols public linkage. I think a 
> follow-up proposal could introduce this feature as well as “conditionally 
> available inlinable”, which I describe in document below. However if people 
> feel strongly we could roll both of them into this proposal, but it would 
> require some more implementation work.
> 
> Slava
> 
>> On Oct 2, 2017, at 1:44 PM, Xiaodi Wu > > wrote:
>> 
>> Very much looking forward to this. Any possibility of rolling in some 
>> version (ha) of @_versioned so that @inlinable functions can reference 
>> internal declarations?
>> 
>> 
>> On Mon, Oct 2, 2017 at 3:31 PM, Slava Pestov via swift-evolution 
>> > wrote:
>> Hi all,
>> 
>> Here is a draft proposal that makes public a feature we’ve had for a while. 
>> Let me know what you think!
>> 
>> Cross-module inlining and specialization ("@inlinable")
>> Proposal: SE- <>
>> Authors: Slava Pestov , Jordan Rose 
>> 
>> Review Manager: TBD
>> Status: Initial pitch
>> Implementation: Already implemented as an underscored attribute @_inlineable
>> Introduction
>> We propose introducing an @inlinable attribute which exports the body of a 
>> function as part of a module's interface, making it available to the 
>> optimizer when referenced from other modules.
>> 
>> Motivation
>> One of the top priorities of the Swift 5 release is a design and 
>> implementation of the Swift ABI. This effort consists of three major tasks:
>> 
>> Finalizing the low-level function calling convention, layout of data types, 
>> and various runtime data structures. The goal here is to maintain 
>> compatibility across compiler versions, ensuring that we can continue to 
>> make improvements to the Swift compiler without breaking binaries built with 
>> an older version of the compiler.
>> 
>> Implementing support for library evolution, or the ability to make certain 
>> source-compatible changes, without breaking binary compatibility. Examples 
>> of source-compatible changes we are considering include adding new stored 
>> properties to structs and classes, removing private stored properties from 
>> structs and classes, adding new public methods to a class, or adding new 
>> protocol requirements that have a default implementation. The goal here is 
>> to maintain compatibility across framework versions, ensuring that framework 
>> authors can evolve their API without breaking binaries built against an 
>> older version of the framework. For more information about the resilience 
>> model, see the library evolution document 
>>  in 
>> the Swift repository.
>> 
>> Stabilizing the API of the standard library. The goal here is to ensure that 
>> the standard library can be deployed separately from client binaries and 
>> frameworks, without forcing recompilation of existing code.
>> 
>> All existing language features of Swift were designed with these goals in 
>> mind. In particular, the implementation of generic types and functions 
>> relies on runtime reified types to allow separate compilation and type 
>> checking of generic code.
>> 
>> Within the scope of a single module, the Swift compiler performs very 
>> aggressive optimization, including full and partial specialization of 
>> generic functions, inlining, and various forms of interprocedural analysis.
>> 
>> On the other hand, across module boundaries, runtime generics introduce 
>> unavoidable overhead, as reified type metadata must be passed between 
>> functions, and various indirect access patterns must be used to manipulate 
>> values of generic type. We believe that for most applications, this overhead 
>> is negligible compared to the actual work performed by the code itself.
>> 
>> However, for some advanced use cases, and in particular for the standard 
>> library, the overhead of runtime generics can dominate any useful work 
>> performed by the library. Examples include the various algorithms defined in 
>> protocol extensions of Sequence and Collection, for instance the mapmethod 
>> of 

Re: [swift-evolution] Enums and Source Compatibility

2017-10-02 Thread Jordan Rose via swift-evolution
I don't think I have anything to say on this topic that I haven't already said:

- Switching exhaustively over non-exhaustive enums is uncommon.
- It's more important for a library to build without errors when its 
dependencies change than it is to get an error. (This doesn't apply to 
warnings, though.)
- Untestable code is dangerous, so having a language feature inherently for 
untestable code seems bad.

None of that negates your points; it just affects the weighting of whether or 
not 'future' or 'switch!' is worth it. However, I've added a link to your email 
in the proposal proper so that the Core Team and wider review audience have a 
chance to decide differently.

Jordan


> On Oct 2, 2017, at 08:25, Vladimir.S via swift-evolution 
>  wrote:
> 
> 
> Sorry to bother, but I still can't understand how the proposed change 
> *without* a 'future' case in switch will change our life and what would be 
> our steps to support our code and to not make our code buggy.
> If I misunderstand something - sorry, please point me on this and I hope this 
> also help some one like me to understand the subject better.
> 
> For example. I use OAuth2 framework, built by Carthage. Did add the 
> OAuth2.framework to my project.
> 
> Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in my 
> code where I switch on each case of such error instance to do my best with 
> error: generate detailed description for user, other additional steps 
> depending on error.
> 
> Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
> Will new cases be added to that enum in future: Most likely Yes.
> Do I need to switch on each case in my code? Yes.
> Can I currently rely on compiler to keep my error processing in sync with 
> error cases defined in framework? Yes.
> Can new cases appear in *run-time* of my app: NO, framework in embedded.
> Will I be able to rely on compiler after the proposed change? No?!
> What should I do to keep my switch in sync with OAuth2Error cases after each 
> update of OAuth2 library(framework)? Manually check if new cases are added?! 
> Configure lint/other tools to help me with this?!
> 
> What I, as a developer, as a consumer of framework, need - is a way to 
> exhaustively switch on *some* external non-exhaustive enums *at the moment of 
> compilation*. And we can accomplish this only(AFAICT) with 'future' case in 
> 'switch'.
> In case we'll have 'future' case my life will not be *worse* for this project 
> : I'll add it to my switch and still can receive help from compiler to keep 
> switch exhaustive.
> 
> I don't support the opinion that we can't introduce 'future' case because of 
> we can't test it:
> 
> 1. Not being able to keep my switch exhaustive when I need this, and so not 
> being able to provide users of my app with best experience - IMO is worse.
> 2. In my particular example, 'future' case will be *never* called, if I 
> understand correctly.
> 3. If switch on non-exhaustive enum is exhaustive by fact, we can't test the 
> 'default' branch also. So, 'future' is in same position here with 'default'
> 4. I believe if we'll decide we need 'future' case - we can suggest a way to 
> call code in that case during the test process.
> 
> Seems like for embedded frameworks we should apply the same rules(regarding 
> enums) as for sources, as we compile the app with concrete binary of 
> framework and there just can't be new cases in enums. No?
> 
> Thank you for your time.
> Vladimir.
> 
> On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:
>>> On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution 
>>> > wrote:
>>> 
>>> I don’t see how it’s impractical. Quite a lot about how the library should 
>>> be optimally compiled and used depends on what you plan to do with it. If 
>>> it’s going to be installed somewhere private and you can guarantee clients 
>>> will always have the latest version, you can assume all data types are 
>>> final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An 
>>> example of that would be a library embedded inside an iOS app bundle. If 
>>> it’s going to be installed somewhere public and expose some API (like 
>>> Apple’s frameworks), then you’re going to have to think about binary 
>>> compatibility.
>>> 
>>> That also means that in-app libraries are optimised as much as they can be, 
>>> and that resilience-related changes on the declaration side can be limited 
>>> to the limited set of Swift developers who truly have to care about that.
>> We do plan on exposing an -enable-resilience flag which basically does what 
>> you describe. When a library is built without -enable-resilience, all types 
>> are assumed to be fixed layout, etc. However, we don’t want language flags 
>> to change language semantics, so exhaustive/nonexhaustive still make sense 
>> even when building without resilience, I think. When you switch over a 

Re: [swift-evolution] Enums and Source Compatibility

2017-10-02 Thread Jordan Rose via swift-evolution
This exists, but it's imperfect: it's possible that an app and its embedded 
frameworks are not all linked against the same version of the SDK, and then the 
system framework has to go with whatever the app does. (It's more important to 
behave consistently within a process.)

It's not impossible to design something like this for Swift and even for 
third-party frameworks, but that particular limitation is inherent. It also 
doesn't always make sense to try to emulate the old behavior—in the first 
system that gains support for DVDs, it's not really worth pretending that 
they're CDs or floppy disks.  I contend that non-exhaustive enums are a fact of 
life for OS frameworks, and still a good thing for open source frameworks.

Jordan


> On Oct 2, 2017, at 10:29, Wallacy via swift-evolution 
>  wrote:
> 
> @Slava
> 
> If my understanding is correct. If I compile my application with the x.y.z 
> version of a Apple System Framework (like Cocoa). And this framework is 
> updated, several calls from my application to the "new" framework version 
> x.z.a behave like in the x.y.z version for compatibility issues right? I see 
> this being mentioned in the Cocoa documentations sometimes.
> 
> Is possible to do this here?
> 
> I usually don't care about non-exhaustive enums. We avoid this even in 
> languages which do not enforce us (using external tools sometimes). But i can 
> understand the "need" in some projects.
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] Enums and Source Compatibility

2017-09-29 Thread Jordan Rose via swift-evolution
(responses to both Jon and Goffredo's most recent point inline)

> On Sep 28, 2017, at 16:15, Goffredo Marocchi  wrote:
> 
> Agreed, I am not seeing this change doing so much good because maybe it could 
> prevent issues Library writers or developers updating libraries without 
> checking things... not trying to be rude and/or non empathetic, but 
> exhaustive enums never struck me as a bad thing and the reasons why they 
> could be bad very quickly leads one to think “maybe you should not have been 
> switching on enums there...”.

I suspect this is correct! Which makes non-exhaustive enums a way to 
communicate that this is probably not a good enum to switch exhaustively on. 
(Since switch statements can be used for other pattern-matching purposes in 
Swift, taking the F# route of disallowing matching cases altogether seems 
unnecessary.)

> On 28 Sep 2017, at 23:57, Jon Shier via swift-evolution 
> > wrote:
> 
>>  Actually, since proper dependency management for Apple platforms has 
>> existed outside of Apple for years now, this likely wouldn’t affect me at 
>> all, as long as the libraries I was using properly followed semantic 
>> versioning. I could keep using the compatible version of the libraries for 
>> however long I needed before moving to the new version and updating my code 
>> to exhaustively check for the new values. So please don’t make this change 
>> thinking you’ll be helping non-Apple framework providers here. Aside from 
>> actual binary compatibility I’m not sure there’s a compatibility case to be 
>> made here. 

>From the semantic versioning point of view, non-exhaustive enums allow you to 
>add a new case while changing only the minor version number and not the major 
>version number. That is certainly an extra feature for non-Apple framework 
>providers; whether or not it's a sufficiently useful one to justify the 
>increased language complexity is, well, part of your evaluation of the 
>proposal.

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-28 Thread Jordan Rose via swift-evolution


> On Sep 22, 2017, at 08:20, Karl Wagner  wrote:
> 
> So I’m in favour of the change, but absolutely not in favour of the keywords. 
> We need to standardise a minimal set of keywords which broadly cover the 
> things library authors need to care about (versioning, sub-typing, inlining, 
> etc). How is “exhaustive” different from “final” or “@_versioned”? How do 
> they interact? I think we could come up with a more coherent message.

As I've mentioned before, unifying all these things is not a good idea because 
they affect clients in different ways. That is, clients need to know about some 
of these and not others, and library authors probably shouldn't be considering 
them all to be equivalent.

- Exhaustivity, of course, affects diagnostics around 'switch' statements.

- The unformalized struct attribute currently spelled '@_fixedLayout' is an 
optimization hint that only matters for binary frameworks*; any struct in a 
library you build from source can have its layout guaranteed to clients.

- The unformalized function attribute currently spelled '@_inlinable' ought to 
be in the same bucket as '@_fixedLayout', but in practice I'm not sure we'll be 
able to make everything inlinable and still support fast compilation (yes, yes, 
go ahead and laugh). This might remain a user-specified attribute for a while, 
in the same way that you have to manually decide to make certain operations 
into `static inline` functions in C-based languages.

- The unformalized attribute currently spelled '@_versioned' is basically 
"public but hidden", in that it makes a declaration part of your ABI without 
making it part of your API. This is related to the other attributes, but I 
don't think it's the same thing. It's also mostly unrelated to what the Library 
Evolution document calls "versioning"; it's standing in for a proper 
availability annotation on a declaration that's 'internal' rather than 'public'.

I could see the latter three being related, but I think there's a definite 
reason to keep exhaustivity separate. It's more similar to existing modifiers 
like 'open' than to ABI- and optimization-related annotations like 
'@_fixedLayout'; it's just too bad that 'open' has the reverse connotations 
from what we need.


That said, I'd actually be fine with `final`. Like exhaustivity, `final` allows 
clients outside the original library to do things they otherwise wouldn't be 
able to: add 'required' initializers. (It also has effects within the module, 
which exhaustivity doesn't, but that seems okay to me.)

Jordan


* I'm using "binary frameworks" to mean "libraries with binary compatibility 
requirements" again; a pre-built library that's embedded with an app doesn't 
run into these issues.___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] Enums and Source Compatibility

2017-09-28 Thread Jordan Rose via swift-evolution


> On Sep 22, 2017, at 17:50, Rex Fenley  wrote:
> 
> It’s really only an issue for people whose frameworks are used across 
> variously-distributed apps (your own, or other peoples’). The typical dynamic 
> library use-case. Those people already know to be careful, so why make 
> non-exhaustive the default for everybody? It’s a fair point.
>  
> Getting this right can be pretty hard. For example, I expect that 
> @_inlineable functions within the enum’s declaring module will also be 
> affected by this (a client could inline the code, then you add an enum 
> case…). At the same time, it’s difficult for a shared library vendor to say 
> definitively that a particular enum will never change (unless it really is a 
> very simple thing, like NSComparisonResult) - almost anything with meaning to 
> how the library works is usually subject to change in case edge-cases are 
> discovered. The analysis of Apple’s frameworks is good data to prove it, but 
> it also makes sense that vendors like to retain as much implementation 
> freedom as possible.
> 
> Just for clarity, this seems like the binary compatibility piece correct? As 
> far as I understand this could only affect a dynamically linked library, but 
> if you're linking a library dynamically, how could you inline a function, 
> since if it's dynamically linked you don't know what to inline?

Karl answered this part already, but this is equivalent to `static inline` 
functions in C. The library is dynamically linked, but specific pieces of it 
are available for clients to inline.


> Also, I've never heard of these kinds of issues in other languages with 
> exhaustive pattern matching. Would be very interested to know how OCaml or 
> Haskell solve this problem?

This is a great question, and I feel a little ashamed that I didn't include a 
proper cross-language study ahead of time!

As far as I can tell, Haskell and OCaml completely punt on this issue. All 
"enums" are exhaustive (the existing Swift behavior), and adding a new "case" 
to a "public enum" is a source-breaking change all the time. (Neither Haskell 
nor OCaml seems to care much about binary compatibility.) This is definitely a 
sign that you can have a successful language without a form of non-exhaustive 
enums other than "protocols". However, I think that just means that people 
never add new cases to existing enums, which is not where we want to end up 
with Swift. Kotlin also falls in this bucket.

Scala's notion of enums is "sealed traits", which is something like a protocol 
that can only be conformed to from a particular file. The compiler then checks 
for all the possible types and allows you to exhaustively switch over them. In 
some sense that means Scala is matching our behavior, in that they're 
"non-exhaustive by default, exhaustive via 'sealed'", but doing that with 
protocols isn't really equivalent because in the non-sealed case someone could 
conform to the protocol from outside the "module".

Rust has an accepted proposal to add non-exhaustive enums 
 
that looks a lot like this one, but where "exhaustive" is still the default to 
not break existing Rust programs. (There are some interesting differences that 
come up in Rust but not Swift; in particular they need a notion of 
non-exhaustive structs because their structs can be decomposed in 
pattern-matching as well.)

There's a C++ proposal about an [[exhaustive]] attribute 
 that roughly 
matches this use case, although of course in C "falling off the end of a 
switch" is perfectly legal. The proposed attribute would merely be a hint to 
compilers for what warnings to emit.

Enums in D are like enums in C, but D distinguishes `switch` from `final 
switch`, and only the latter is exhaustive. (That is, it's a client-side 
decision.)

F# unions either expose all of their "cases" or none of them 
. 
I guess the Swift equivalent of this would be not allowing you to switch on 
such an enum at all, as if it were a struct with private fields.

The C# docs have a nice section on how the language isn't very helpful 

 for non-exhaustive enums. Objective-C, of course, is in the same bucket, 
though we (Apple) could start doing things with the 'enum_extensibility' 
attribute we just added.

---

I'm not sure this'll change anyone's mind, but it was still a good exercise. 
I'll add this to the proposal as well, so people don't have to dig through the 
thread to find this information.

Jordan

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-28 Thread Jordan Rose via swift-evolution


> On Sep 27, 2017, at 16:48, Jessie Serrino  wrote:
> 
> Hi there,
> 
> Just want to circle back on a few things.
> 
> You mentioned that library vendors communicate deprecation notices, but this 
> is very prone to error. If someone misses the notice that a library puts out 
> to communicate that the contracts of the enum have changed, this could break 
> existing functionality they have already built. 
> 
> You also mention that it's really hard for a library developer to predict how 
> their enum will change, but it's even harder for a library developer to fully 
> predict how a different user will use their library. As a developer, I don't 
> want to have to look at the release notes to find out when a library has 
> changed its contracts-- I want it to alert me in the most aggressive way 
> possible to make sure that major changes to the library have been captured in 
> my own code.

Hi, Jessie. This is a reasonable view, but in our experience it's not the most 
important thing. What we've seen is that people want their code to keep 
building after an update, and in particular they want their dependencies to 
keep building. That is, if your app depends on HomeworkKit, and HomeworkKit 
adds a new case, you probably want to know about it. But if someone's app 
depends on CoreAcademia, and CoreAcademia depends on HomeworkKit, they're 
probably just going to be upset if CoreAcademia no longer builds. In practice, 
that person/team stops updating HomeworkKit until there's a CoreAcademia update 
available, or possibly stops working on their app altogether, to avoid editing 
someone else's library.

(Again, this becomes even more critical if HomeworkKit is a part of the OS, in 
which case there's not even a choice whether or not to update. But I can see 
that being handled separately.)

I'm not completely against having an additional notion of an 
"otherwise-exhaustive" switch, as discussed under "Preserve exhaustiveness 
diagnostics for non-exhaustive enums". But that's a feature we can add later, 
whereas differentiating exhaustive and non-exhaustive enums is something that 
must be done before Swift libraries can be part of the OS.

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-21 Thread Jordan Rose via swift-evolution

> On Sep 20, 2017, at 16:15, Dave DeLong  wrote:
> 
> Hi Jordan,
> 
> One thing I’m still not clear on exhaustive vs non-exhaustive…
> 
> What will stop a developer from releasing an exhaustive enum in version 1 of 
> their library, and then adding a new case in version 2?
> 
> With ABI stability and libraries getting updated independently of apps, this 
> can be a major problem.

We have some ideas to deal with this, though nothing promised yet:

- A checker that can compare APIs across library versions, using swiftmodule 
files or similar.
- Encoding the layout of a type in a symbol name. We could have clients link 
against this symbol so that they’d fail to launch if it changes, or just check 
the list of exported symbols to make sure it didn’t change.

The feature’s useful even if we have to do it by hand for now, but it’s a good 
question to ask. I’ll mention this in the proposal under “Future directions”.

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-18 Thread Jordan Rose via swift-evolution
I don't think unit tests help much here. In the case where an 'exhaustive' enum 
ought to be 'nonexhaustive', unit tests wouldn't help you catch the issue until 
you tried adding a new case. And in both cases, a unit test would only help if 
you tried to switch exhaustively over the enum, something you may not think to 
write (depending on what the enum is, of course).

In either direction, there's a potential error of omission, and if you've 
"omitted" the annotation from your library code I wouldn't assume you'd 
remember to check that specific thing in a unit test.

Jordan


> On Sep 16, 2017, at 00:44, Goffredo Marocchi <pana...@gmail.com> wrote:
> 
> Sorry for being naive, but aside from open (and the decision not to make it 
> the default which again hurts the users of the library),  wouldn’t the 
> playground library example be ok if the framework author had validated it 
> with unit tests?
> 
> Sent from my iPhone
> 
> On 16 Sep 2017, at 01:07, Jordan Rose via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> 
>> Hi, Rex. I definitely agree that 'exhaustive' is the right model for a 
>> multi-module app; indeed, there's no real reason for a single project to do 
>> anything else. However, it is not always the right behavior for libraries 
>> that actually get distributed, whether as source or as binary. In this case 
>> we want to minimize the error of omission: in the app case, forgetting 
>> "exhaustive" is an annoyance that you notice and fix once across your code 
>> base, but in the library case forgetting the "default case" means putting 
>> out a source-breaking release, and for libraries that have binary 
>> compatibility constraints there's no recourse at all.
>> 
>> While most of the proposal deals with the experience we've had with the 
>> Apple SDKs (as written in Objective-C), we actually have run into this case 
>> in Swift already. The Swift Playgrounds app comes with a framework, 
>> PlaygroundSupport, that can be used from within a playground. It's important 
>> that when they upgrade the app, existing playgrounds don't break, since the 
>> end user may not have access to the entire code of the playground. (Remember 
>> that playgrounds are often authored by one developer or group, but then run 
>> and modified by someone else with a much lower skill level!) That means that 
>> PlaygroundSupport can't currently vend any enums that they expect playground 
>> authors to exhaustively switch over.
>> 
>> (And to make it even more specific—and appealing—one of the enums they were 
>> considering would be a representation of the Swift AST. This can obviously 
>> change from release to release, but previous switch statements should stay 
>> valid.)
>> 
>> Now, this is an example we know about, so we could certainly make it 
>> explicitly non-exhaustive. But in general we're in the same situation as 
>> 'open': if we want to be friendly to library authors, we need to make the 
>> default thing be the one that promises less, even if it means a bit of extra 
>> work in the "I-actually-own-everything" case.
>> 
>> Best,
>> Jordan
>> 
>> 
>>> On Sep 15, 2017, at 15:47, Rex Fenley <r...@remind101.com 
>>> <mailto:r...@remind101.com>> wrote:
>>> 
>>> Hey Jordan,
>>> 
>>> Thank you for the time writing this up. I've been following along to the 
>>> discussion somewhat closely and have kept silent because `exhaustive` was 
>>> originally set to be the default for enums. However, that changed and so 
>>> I'd like to voice my opinion, I frankly don't like this idea.
>>> 
>>> At remind we use algebraic data types religiously for managing state and 
>>> data and rely on exhaustive pattern matching to guarantee we're handling 
>>> all states in our code. We're splitting out our code across modules and 
>>> having this guarantee has been a joy to work with.
>>> 
>>> The benefit of making nonexhaustive the default for Swift 5 across all 
>>> multi-module code (besides C code) seems minimal to me. If a developer 
>>> feels like they're unnecessarily managing enum cases, they can simply add a 
>>> `default` case whenever they please. This is already the case and I'm 
>>> curious if there's every been any complaints about this and what they would 
>>> be. I'd prefer to be cautious and force exhaustive pattern matching in all 
>>> possible cases and leave it up to the developer to choose not to.
>>> 
>>> Ideally in my mind, these keyword

Re: [swift-evolution] Enums and Source Compatibility

2017-09-18 Thread Jordan Rose via swift-evolution
As mentioned, the right way to do this is with a struct:

public struct ConnectionDictionaryKey: RawRepresentable, Hashable {
  public var rawValue: String
  public init(rawValue: String) { … }
  public init(_ rawValue: String) { … }
}
extension ConnectionDictionaryKey {
  static let hostName = ConnectionDictionaryKey("hostname")
  static let databaseName = ConnectionDictionaryKey("database")
  // …
}

It is definitely more verbose than an enum, and it may be worth a separate 
proposal to deal with that! But it is not part of this proposal, and what 
you're describing isn't really an enum.

Jordan


> On Sep 16, 2017, at 15:51, Kenny Leung via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Oops, forgot something:
> 
> "Can there be a kind of open enum where you can add new cases in extensions?”
> 
> I have a use case for this. I’m trying to write a database ORM with abstract 
> API and concrete instances for different database. So I have defined:
> 
> open class Database {
>init(connectionDictionary: [ConnectionDictionaryKey:String]) {
> self.connectionDictionary = connectionDictionary;
> }
> }
> 
> Where I have ConnectionDictionaryKey defined as an enum, with values like 
> .hostName, .databaseName, .userName, .password, .databaseName, etc…
> 
> But concrete databases may have other options that need to be added to the 
> connection dictionary. It would be nice if they could just extend 
> ConnectionDictionaryKey with new cases.
> 
> -Kenny
> 
> 
>> On Sep 16, 2017, at 3:35 PM, Kenny Leung via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> In general, I agree with everything in the proposal.
>> 
>> I’d like to propose these alternative extensions for clients:
>> 
>> 1) As a client of an enum, I’d like to know in the future when a new value 
>> has been added to an enum, since I may have to do something about it. How 
>> about adding the “exhaustive” keyword to be used in the switch statement? 
>> Like
>> 
>> exhaustive switch excuse {
>> case eatenByPet:
>> // …
>> case thoughtItWasDueNextWeek:
>> // …
>> default:
>> // …
>> }
>> 
>> If exhaustive is used, there would be a warning if all cases aren’t covered 
>> *even though default exists*. This means that I as the client thought I had 
>> everything covered when I wrote this code.
>> 
>> As already mentioned, this makes the default case un-testable, which brings 
>> me to
>> 
>> 2) All non-exhaustive enums should have the pseudo value “default” that can 
>> be used just like a regular value. This would allow you to write code like:
>> 
>> teacher.failedToHandInHomework(excuse: .default)
>> 
>> which would allow you to trip the default case in any code you may write.
>> 
>> -Kenny
>> 
>> 
>>> On Sep 13, 2017, at 12:17 PM, Jordan Rose via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> Proposal updated, same URL: 
>>> https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
>>>  
>>> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md>.
>>> 
>>> Thanks again for all the feedback so far, everyone!
>>> Jordan
>>> 
>>> 
>>>> On Sep 12, 2017, at 17:55, Jordan Rose via swift-evolution 
>>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>> 
>>>> Sorry, I got distracted by other tasks! Both the discussion here and 
>>>> within Apple has moved towards making "non-exhaustive" the default, which, 
>>>> to be honest, I too think is the best design. I'll update the proposal 
>>>> today to reflect that, though I still want to keep both the 
>>>> "nonexhaustive" and "exhaustive" keywords for Swift 4 compatibility for 
>>>> now (or whatever we end up naming them). The compatibility design is a 
>>>> little less ambitious than Brent's; as currently proposed, Swift 4 mode 
>>>> continues to default to 'exhaustive' all the time, even in the actual 
>>>> Swift 5 release.
>>>> 
>>>> I still want to respond to Brent's points directly, but I think you and 
>>>> Vladimir have done a good job discussing them already. I'll send out the 
>>>> updated proposal tomorrow, after I ha

Re: [swift-evolution] Enums and Source Compatibility

2017-09-18 Thread Jordan Rose via swift-evolution


> On Sep 16, 2017, at 15:35, Kenny Leung via swift-evolution 
>  wrote:
> 
> In general, I agree with everything in the proposal.
> 
> I’d like to propose these alternative extensions for clients:
> 
> 1) As a client of an enum, I’d like to know in the future when a new value 
> has been added to an enum, since I may have to do something about it. How 
> about adding the “exhaustive” keyword to be used in the switch statement? Like
> 
> exhaustive switch excuse {
> case eatenByPet:
> // …
> case thoughtItWasDueNextWeek:
> // …
> default:
> // …
> }
> 
> If exhaustive is used, there would be a warning if all cases aren’t covered 
> *even though default exists*. This means that I as the client thought I had 
> everything covered when I wrote this code.
> 
> As already mentioned, this makes the default case un-testable, which brings 
> me to
> 
> 2) All non-exhaustive enums should have the pseudo value “default” that can 
> be used just like a regular value. This would allow you to write code like:
> 
> teacher.failedToHandInHomework(excuse: .default)
> 
> which would allow you to trip the default case in any code you may write.

Brent came up with this idea as well, but after thinking it through Joe Groff 
and I found that it doesn't really work in practice:

> It’s going to be very common to have a future value and hand it right back to 
> the framework without looking at it, for example:
> 
> override func process(_ transaction: @testable Transaction) {
>   switch transaction {
>   case .deposit(let amount):
> // …
>   case .withdrawal(let amount):
> // …
>   default:
> super.process(transaction) // hmm…
>   }
> }
> 
> So just making it to the ‘default’ case doesn’t guarantee that it’s testable 
> in practice.

I'll add the actual code here to the "Testing invalid cases" section under 
"Alternatives considered", since that's a clearer example than the paragraph I 
wrote. (Oh, and the "exhaustive switch" is equivalent to the section on 
"'future' cases".)

Thanks for the feedback!
Jordan___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] Enums and Source Compatibility

2017-09-18 Thread Jordan Rose via swift-evolution
That's pretty much the same as this proposal except you don't have the new 
keyword. I'm not sure why that really makes a difference, since they're 
obviously paired, and it would limit existing libraries from declaring 
exhaustive enums until they've moved entirely to Swift 5 mode. I think the 
current proposal makes sense as is.

Jordan


> On Sep 16, 2017, at 01:55, David Hart <da...@hartbit.com> wrote:
> 
> I’m still very much bothered by having 2 new keywords. I would really prefer 
> the following plan:
> 
> Exhaustive by default in Swift 4
> No new keyword in Swift 4 to change that behaviour
> Non-exhaustive by default outside the module in Swift 5
> exhaustive keyword to change the default behaviour
> 
> Like that, we don’t need nonexhaustive.
> 
> Thoughts?
> David.
> 
>> On 13 Sep 2017, at 21:16, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> Proposal updated, same URL: 
>> https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
>>  
>> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md>.
>> 
>> Thanks again for all the feedback so far, everyone!
>> Jordan
>> 
>> 
>>> On Sep 12, 2017, at 17:55, Jordan Rose via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> 
>>> Sorry, I got distracted by other tasks! Both the discussion here and within 
>>> Apple has moved towards making "non-exhaustive" the default, which, to be 
>>> honest, I too think is the best design. I'll update the proposal today to 
>>> reflect that, though I still want to keep both the "nonexhaustive" and 
>>> "exhaustive" keywords for Swift 4 compatibility for now (or whatever we end 
>>> up naming them). The compatibility design is a little less ambitious than 
>>> Brent's; as currently proposed, Swift 4 mode continues to default to 
>>> 'exhaustive' all the time, even in the actual Swift 5 release.
>>> 
>>> I still want to respond to Brent's points directly, but I think you and 
>>> Vladimir have done a good job discussing them already. I'll send out the 
>>> updated proposal tomorrow, after I have a little more time to think about 
>>> #invalid.
>>> 
>>> Thanks for putting time into this!
>>> Jordan
>>> 
>>> 
>>>> On Sep 9, 2017, at 17:34, Rod Brown <rodney.bro...@icloud.com 
>>>> <mailto:rodney.bro...@icloud.com>> wrote:
>>>> 
>>>> Jordan,
>>>> 
>>>> Do you have any other thoughts about the ongoing discussion here, 
>>>> especially regarding Chris’ comments? As you’re the one pushing this 
>>>> forward, I’d really like to know what your thoughts are regarding this?
>>>> 
>>>> - Rod
>>> 
>>> ___
>>> swift-evolution mailing list
>>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto: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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-18 Thread Jordan Rose via swift-evolution


> On Sep 16, 2017, at 11:35, Christopher Kornher via swift-evolution 
>  wrote:
> 
> 
> 
> 
> 
>> On Sep 16, 2017, at 11:28 AM, Christopher Kornher via swift-evolution 
>> > wrote:
>> 
>>> 
>>> On Sep 16, 2017, at 8:41 AM, Rod Brown via swift-evolution 
>>> > wrote:
>>> 
>>> 
>>> On 16 Sep 2017, at 7:22 pm, Goffredo Marocchi via swift-evolution 
>>> > wrote:
>>> 
 I am still unsure why we are choosing again a default that protects 
 library writers more than library users where it is reasonable to expect 
 the former to have better mastery of the language, to architect a library 
 with some scalability, and ability to add unit test to cover themselves 
 from issues than the latter.
>>> 
>>> Because protecting library owners protects library users.
>> 
>> If a library writer can’t remember to declare non-exhaustive enums as such, 
>> they probably will forget many more important aspects of creating a library. 
>> They probably should not be writing libraries. Arguments like this make 
>> sense on the surface, but creating libraries involves hundreds or thousands 
>> of decisions. I wish you luck in making that process idiot proof. A library 
>> linter could easily warn that exposed enums are exhaustive. The exhaustive 
>> keyword should be optional to make the decision obvious and suppress 
>> warnings. Complicating the user experience in a vain attempt to make 
>> “expert" coding safer is misguided.
> 
> This may be a little harsh, but there don’t seem to be many advocates for 
> novice and “ordinary” application developers on this list. That is not 
> unexpected given the number of extremely knowledgeable compiler and library 
> developers on this list (for whom I have the highest respect). I believe that 
> there are more creative (and probably more difficult to build) possible 
> solutions to some of the tough problems in Swift’s future. In that spirit, 
> see below.

It's definitely good to consider the effects on normal application developers!


> 
>> 
>> 
>>> 
>>> If you declare it is exhaustive and it was an oversight, and then realise 
>>> after the fact that you are wrong, you have to open it up. This will break 
>>> third party apps. It will be disallowed by the ABI compatibility 
>>> requirements.
>>> 
>>> If you declare it isn’t exhaustive due to an oversight (or perhaps you’re 
>>> just not sure yet), and then realise after the fact it is exhaustive, you 
>>> can close it up. This will not break third party apps. It will also be 
>>> allowed for ABI compatibility.
>>> 
>>> This benefits everyone. Make library owners choose a guarantee, rather than 
>>> be defaulted into it. Much like they have to declare choose to declare 
>>> “final” on a class: you can’t retroactively reneg that promise: it will 
>>> break everyone who assumed it to be the case!
>> 
>> It does not benefit the creation of 90+% of enums. It is one more arcane 
>> rule for the vast majority of developers.
> 
> The Swift compiler could offer a “strict enum exhaustiveness” (bikeshedding 
> not included) switch that could be enabled by default for library targets and 
> disabled by default for “application” targets. The switch would make not 
> explicitly specifying exhaustiveness an error or warning when enabled. 
> Perhaps this could be combined with other options that would tailor the 
> development experience for library/application developers. This would help 
> avoid “zero-sum” choices between benefitting library or application 
> developers in the future.
> 
> Xcode and the SPM should be able to distinguish between the target types and 
> generate the proper defaults. I do not believe that this is too mysterious 
> for developers. There would be learning step for developers wiring their 
> first library, but that is not necessarily a bad thing since creating a 
> reusable library requires a different mindset than creating an application.

Right now we have this notion, but the difference between library and app is 
signified by "public". My opinion is that between the choices of "multi-module 
applications have to deal with everything SwiftPM packages do" and "there are 
different rules for multi-module applications and for SwiftPM packages", the 
former is preferable just in terms of overall complexity in the language. It's 
certainly a trade-off! But that's what I'm proposing, and it should be very 
clear what to do when multi-module app developers encounter the additional 
rules that come with, well, having multiple modules.

(We're already likely to have an extra distinction between "libraries built 
with support for binary compatibility" and "libraries built to be distributed 
with their clients", but it's still better if that too avoids splitting the 
language into dialects.)


Re: [swift-evolution] Enums and Source Compatibility

2017-09-18 Thread Jordan Rose via swift-evolution
That is in fact what the proposal states:

> Currently, adding a new case to an enum is a source-breaking change, which is 
> very inconvenient for library authors. This proposal aims to distinguish 
> between enums that are exhaustive (meaning they will never get any new cases) 
> and those that are non-exhaustive, and to ensure that clients handle any 
> future cases when dealing with the latter. This change only affects clients 
> from outside the original module.

> When a client tries to switch over a nonexhaustive enum, they must include a 
> default case unless the enum is declared in the same module as the switch.

Do you have any suggestions on how to make this clearer in the proposal?

Jordan


> On Sep 15, 2017, at 21:40, Jon Shier <j...@jonshier.com> wrote:
> 
>   In that case Jordan, can Swift not treat it like open? i.e. Internally 
> to a module, unmarked enums are still exhaustive by default, but when made 
> public and used beyond the module, it becomes non-exhaustive? I think this 
> has been discussed before and perhaps discarded as confusing, but it doesn’t 
> seem to be any more confusing than open being the default internally and 
> closed the default publicly. Otherwise you’re essentially forcing what is 
> likely the vast majority of enum usage to adopt a bit of boilerplate that 
> will only be used by the vast minority of libraries (almost entirely 
> libraries shipped by Apple). 
> 
> 
> Jon
> 
>> On Sep 15, 2017, at 8:07 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> Hi, Rex. I definitely agree that 'exhaustive' is the right model for a 
>> multi-module app; indeed, there's no real reason for a single project to do 
>> anything else. However, it is not always the right behavior for libraries 
>> that actually get distributed, whether as source or as binary. In this case 
>> we want to minimize the error of omission: in the app case, forgetting 
>> "exhaustive" is an annoyance that you notice and fix once across your code 
>> base, but in the library case forgetting the "default case" means putting 
>> out a source-breaking release, and for libraries that have binary 
>> compatibility constraints there's no recourse at all.
>> 
>> While most of the proposal deals with the experience we've had with the 
>> Apple SDKs (as written in Objective-C), we actually have run into this case 
>> in Swift already. The Swift Playgrounds app comes with a framework, 
>> PlaygroundSupport, that can be used from within a playground. It's important 
>> that when they upgrade the app, existing playgrounds don't break, since the 
>> end user may not have access to the entire code of the playground. (Remember 
>> that playgrounds are often authored by one developer or group, but then run 
>> and modified by someone else with a much lower skill level!) That means that 
>> PlaygroundSupport can't currently vend any enums that they expect playground 
>> authors to exhaustively switch over.
>> 
>> (And to make it even more specific—and appealing—one of the enums they were 
>> considering would be a representation of the Swift AST. This can obviously 
>> change from release to release, but previous switch statements should stay 
>> valid.)
>> 
>> Now, this is an example we know about, so we could certainly make it 
>> explicitly non-exhaustive. But in general we're in the same situation as 
>> 'open': if we want to be friendly to library authors, we need to make the 
>> default thing be the one that promises less, even if it means a bit of extra 
>> work in the "I-actually-own-everything" case.
>> 
>> Best,
>> Jordan
>> 
>> 
>>> On Sep 15, 2017, at 15:47, Rex Fenley <r...@remind101.com 
>>> <mailto:r...@remind101.com>> wrote:
>>> 
>>> Hey Jordan,
>>> 
>>> Thank you for the time writing this up. I've been following along to the 
>>> discussion somewhat closely and have kept silent because `exhaustive` was 
>>> originally set to be the default for enums. However, that changed and so 
>>> I'd like to voice my opinion, I frankly don't like this idea.
>>> 
>>> At remind we use algebraic data types religiously for managing state and 
>>> data and rely on exhaustive pattern matching to guarantee we're handling 
>>> all states in our code. We're splitting out our code across modules and 
>>> having this guarantee has been a joy to work with.
>>> 
>>> The benefit of making nonexhaustive the default for Swift 5 across all 
>>> multi-modu

Re: [swift-evolution] Enums and Source Compatibility

2017-09-15 Thread Jordan Rose via swift-evolution
Hi, Rex. I definitely agree that 'exhaustive' is the right model for a 
multi-module app; indeed, there's no real reason for a single project to do 
anything else. However, it is not always the right behavior for libraries that 
actually get distributed, whether as source or as binary. In this case we want 
to minimize the error of omission: in the app case, forgetting "exhaustive" is 
an annoyance that you notice and fix once across your code base, but in the 
library case forgetting the "default case" means putting out a source-breaking 
release, and for libraries that have binary compatibility constraints there's 
no recourse at all.

While most of the proposal deals with the experience we've had with the Apple 
SDKs (as written in Objective-C), we actually have run into this case in Swift 
already. The Swift Playgrounds app comes with a framework, PlaygroundSupport, 
that can be used from within a playground. It's important that when they 
upgrade the app, existing playgrounds don't break, since the end user may not 
have access to the entire code of the playground. (Remember that playgrounds 
are often authored by one developer or group, but then run and modified by 
someone else with a much lower skill level!) That means that PlaygroundSupport 
can't currently vend any enums that they expect playground authors to 
exhaustively switch over.

(And to make it even more specific—and appealing—one of the enums they were 
considering would be a representation of the Swift AST. This can obviously 
change from release to release, but previous switch statements should stay 
valid.)

Now, this is an example we know about, so we could certainly make it explicitly 
non-exhaustive. But in general we're in the same situation as 'open': if we 
want to be friendly to library authors, we need to make the default thing be 
the one that promises less, even if it means a bit of extra work in the 
"I-actually-own-everything" case.

Best,
Jordan


> On Sep 15, 2017, at 15:47, Rex Fenley  wrote:
> 
> Hey Jordan,
> 
> Thank you for the time writing this up. I've been following along to the 
> discussion somewhat closely and have kept silent because `exhaustive` was 
> originally set to be the default for enums. However, that changed and so I'd 
> like to voice my opinion, I frankly don't like this idea.
> 
> At remind we use algebraic data types religiously for managing state and data 
> and rely on exhaustive pattern matching to guarantee we're handling all 
> states in our code. We're splitting out our code across modules and having 
> this guarantee has been a joy to work with.
> 
> The benefit of making nonexhaustive the default for Swift 5 across all 
> multi-module code (besides C code) seems minimal to me. If a developer feels 
> like they're unnecessarily managing enum cases, they can simply add a 
> `default` case whenever they please. This is already the case and I'm curious 
> if there's every been any complaints about this and what they would be. I'd 
> prefer to be cautious and force exhaustive pattern matching in all possible 
> cases and leave it up to the developer to choose not to.
> 
> Ideally in my mind, these keywords won't be necessary. All Swift enums will 
> remain as they are, exhaustively pattern matched by default. Enums from C 
> code will be explicitly nonexhaustive in all cases.
> 
> -- 
> Rex Fenley  |  IOS DEVELOPER
> 
> 
> Remind.com  |  BLOG   |  
> FOLLOW US   |  LIKE US 
> 
___
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


Re: [swift-evolution] Subscripts assignable to closure vars

2017-09-15 Thread Jordan Rose via swift-evolution


> On Sep 15, 2017, at 13:00, John McCall via swift-evolution 
>  wrote:
> 
>> 
>> On Sep 15, 2017, at 3:45 PM, Joanna Carter via swift-evolution 
>> > wrote:
>> 
>> Just came across this.
>> 
>> I want to be able to hold onto the reference to a subscript "method" for 
>> later use.
>> 
>> Assigning the subscript to a var in the init of a type raises a segmentation 
>> fault.
>> 
>> Should this - could this - be allowed?
> 
> It really shouldn't be allowed.  I think KeyPaths are the intended language 
> solution here.
> 
> Please file a bug about the crash, though.

The crash is already fixed in master, thanks to Alex Hoppen's work on making 
actual subscripts distinct from the name "subscript".

I think John's right that this should not be allowed. After all, a subscript 
may have both a getter and a setter, and it's not immediately obvious from your 
syntax which one you mean.

We could invent some kind of answer for this (including simply just checking 
the contextual type), but it would be nice™ if any such solution also had a 
good answer for properties. Or we could just make key paths and closures work a 
little better together, which has also been discussed on the list.

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-15 Thread Jordan Rose via swift-evolution


> On Sep 14, 2017, at 20:59, Chris Lattner <clatt...@nondot.org> wrote:
> 
> 
>> On Sep 13, 2017, at 12:17 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> Proposal updated, same URL: 
>> https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
>>  
>> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md>.
>> 
>> Thanks again for all the feedback so far, everyone!
> 
> Hi Jordan,
> 
> I’d appreciate it if you could look at the comments I made upthread and 
> respond to them.  Thanks.

Hi, Chris. Sorry for not responding directly – I assumed because your model was 
very close to this revised proposal, you wouldn't be expecting a direct 
response. At this point, I think the only discrepancy between your version and 
mine is that you'd use a shared annotation 'fragile' (or whatever) rather than 
an enum-specific annotation 'exhaustive' (or whatever). There are a few reasons 
why I didn't go with that:

- Unlike the other possible "fragility attributes" described in the Library 
Evolution <http://jrose-apple.github.io/swift-library-evolution/> plan, 
'exhaustive' changes the behavior of the type in a way that's visible to 
clients. Stating that a struct has a fixed set of stored properties, or that a 
function's definition can be inlined into a caller, acts purely as an 
optimization; it doesn't change what client source code can or cannot do with a 
type.

- Because exhaustivity affects clients, it's something that is useful for 
libraries distributed as source* as well as those with binary-compatibility 
concerns. If such a library provides an exhaustive enum in its public API, 
adding a new case would be a source-breaking change and require a major version 
bump (if the library is using semantic versioning <http://semver.org/>). 
Therefore, just like open and non-open classes, it is beneficial to distinguish 
between the exhaustive and non-exhaustive situations even for libraries 
distributed as source. Such libraries should have no need for the other 
fragility attributes because we expect to be able to perform those 
optimizations by default.

I really wish 'open' made sense here, because that's the closest analogous 
situation, but it just doesn't. So we need a separate annotation (although as 
mentioned in the proposal I'm okay with reusing 'final').

It does help now that `public enum` works without any further annotation; 
that's due to your feedback as well as pushes within Apple.

Jordan


* I'm saying "libraries distributed as source", but really this applies to any 
libraries that get packaged with the end product, usually an app. The real 
condition here is whether a client must be rebuilt to use a new version of the 
library, i.e. "libraries without binary compatibility concerns". These aren't 
officially supported by Apple today, but the model shouldn't leave them out.

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-13 Thread Jordan Rose via swift-evolution
Proposal updated, same URL: 
https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
 
<https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md>.

Thanks again for all the feedback so far, everyone!
Jordan


> On Sep 12, 2017, at 17:55, Jordan Rose via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Sorry, I got distracted by other tasks! Both the discussion here and within 
> Apple has moved towards making "non-exhaustive" the default, which, to be 
> honest, I too think is the best design. I'll update the proposal today to 
> reflect that, though I still want to keep both the "nonexhaustive" and 
> "exhaustive" keywords for Swift 4 compatibility for now (or whatever we end 
> up naming them). The compatibility design is a little less ambitious than 
> Brent's; as currently proposed, Swift 4 mode continues to default to 
> 'exhaustive' all the time, even in the actual Swift 5 release.
> 
> I still want to respond to Brent's points directly, but I think you and 
> Vladimir have done a good job discussing them already. I'll send out the 
> updated proposal tomorrow, after I have a little more time to think about 
> #invalid.
> 
> Thanks for putting time into this!
> Jordan
> 
> 
>> On Sep 9, 2017, at 17:34, Rod Brown <rodney.bro...@icloud.com> wrote:
>> 
>> Jordan,
>> 
>> Do you have any other thoughts about the ongoing discussion here, especially 
>> regarding Chris’ comments? As you’re the one pushing this forward, I’d 
>> really like to know what your thoughts are regarding this?
>> 
>> - Rod
> 
> ___
> 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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-13 Thread Jordan Rose via swift-evolution

> On Sep 13, 2017, at 05:21, Brent Royal-Gordon  wrote:
> 
>> On Sep 12, 2017, at 6:30 PM, Jordan Rose > > wrote:
>> 
>> It gets a little tricky if layout matters—Optional fits exactly 
>> in a single word-sized value, but Optional does not on 
>> Apple platforms—but that just means it should be opt-in or tied to 
>> -enable-testing in some way.
> 
> 
> I forgot to state this explicitly, but I agree—unless the module was compiled 
> with -enable-testing, the generated code should not permit #invalid values 
> and would be identical to a version without any @testable parameters/types.
> 
> Here's a more explicit sketch of a design for this feature (albeit one that 
> has some impact on the type system and a couple weird corners):
> 
>   • `@testable T` is a supertype of `T` which, when the module is 
> compiled with `-enable-testing`, has an additional `#invalid` inhabitant. (We 
> can bikeshed `@testable` and `#invalid` some other time.) Notionally, 
> `@testable` is sort of like an enum which has one case (`valid(Wrapped)`) in 
> a non-`-enable-testing` build, and an additional case (`invalid`) in an 
> `-enable-testing` build.
> 
>   • `T` implicitly converts to `@testable T`; `@testable T` can be 
> explicitly downcast to `T`.* When `-enable-testing` is *not* provided, these 
> downcasts will always succeed, and the trap in `as!` or the code for a `nil` 
> result from `as?` are unreachable. We should ignore and potentially optimize 
> away this unreachable code without warning about it.
> 
>   • Any pattern that matches against `T` can also match against 
> `@testable T` with no alteration. Only `_` or a capture can match 
> `#invalid`.** Otherwise, `#invalid` values will be handled by the `default` 
> case of a `switch` or the `else` block of an `if` or `guard`.
> 
>   • A given `@testable T` value (i.e. property, variable, subscript, 
> parameter, return value, etc.) may only be assigned `#invalid` if it is 
> either in the current module or is in a module imported with `@testable 
> import`.
> 
>   • When `-enable-testing` is *not* provided, all code which creates an 
> `#invalid` value must be unreachable. This is even true in `default:` cases 
> and other constructs which could be reached by unknown future values of a 
> type. Only constructs like `guard let t = testableT as? T else { return 
> #invalid }` can be successfully compiled with `-enable-testing` disabled.
> 
> The memory representation of `#invalid` does not have to be the same for all 
> types, so it could try to find a spare bit or bit pattern that's unused in 
> the original type (as long as, for non-exhaustive enums, it also avoids using 
> any bit pattern a future version of the type *might* use). Or, for 
> simplicity, we could just add a tag byte unconditionally. This tag byte would 
> only be needed when built with `-enable-testing`, so basically only debug 
> builds would pay this price, and only in places where the author explicitly 
> asked to be able to test with `#invalid` values.
> 
> 
> * There's an argument to be made for an IUO-style implicit conversion from 
> `@testable Foo` to `Foo` which traps on `#invalid`. This seems dangerous to 
> me, but on the other hand, you should only ever encounter it in testing or 
> development, never in production.
> 
> ** I'm not sure captures can work here—wouldn't they still be the `@testable` 
> type?—so I'm actually wondering if we should introduce a subtle distinction 
> between `case _`/`case let x` and `default`: the former cannot match 
> `#invalid`, while the latter can. That would be a little bit…odd, though.

Thanks for working this out. This matches the intuitions I was having, and also 
finds a point that’s pretty concerning:

> * There's an argument to be made for an IUO-style implicit conversion from 
> `@testable Foo` to `Foo` which traps on `#invalid`. This seems dangerous to 
> me, but on the other hand, you should only ever encounter it in testing or 
> development, never in production.

It’s going to be very common to have a future value and hand it right back to 
the framework without looking at it, for example:

override func process(_ transaction: @testable Transaction) {
  switch transaction {
  case .deposit(let amount):
// …
  case .withdrawal(let amount):
// …
  default:
super.process(transaction) // hmm…
  }
}

So just making it to the ‘default’ case doesn’t guarantee that it’s testable in 
practice.

In any case, a model like this can be added later without breaking source or 
binary compatibility, so I think I’m going to leave it out of the proposal for 
now. I’ll mention it in “Alternatives considered”.

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-12 Thread Jordan Rose via swift-evolution
Just getting around to this again. Thanks for putting a fair amount of thought 
in! I'm putting short section-by-section responses here, though the other 
discussion has already covered a fair amount. I'm planning to have a new 
revision of the proposal up tomorrow as well.


> On Sep 6, 2017, at 05:53, Brent Royal-Gordon <br...@architechies.com> wrote:
> 
>> On Sep 5, 2017, at 5:19 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> I've taken everyone's feedback into consideration and written this up as a 
>> proposal: 
>> https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
>>  
>> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md>.
>>  The next step is working on an implementation, but if people have further 
>> pre-review comments I'd be happy to hear them.
> 
> I disagree with the choice of `exhaustive` and `nonexhaustive`. They are too 
> long; the more resilient keyword is longer than the more fragile one (and 
> difficult to read!); and they don't match the clang annotation. We may have 
> to compromise on one or two of these, but the combination of all three ought 
> to be considered disqualifying.
> 
> I think `final`/`nonfinal`, `total`/`partial`, `fixed`/? or `permanent`/? are 
> all better because they're shorter, although they all have problems with 
> their antonyms. `candid`/`coy` or `candid`/`shy` produce the right soft 
> default, but are kind of weirdly figurative.

I'm very happy for the review to end up settling on something else. It also 
sounds like the direction to go in will make 'nonexhaustive' the default in 
Swift 5 mode, so it at least wins on the second criterion in the long run.


> 
> But I don't think a change of keywords will fix everything here. 
> Fundamentally, I am not convinced that source compatibility of `switch` 
> statements should be weighed so heavily. Based on your survey of Foundation, 
> you suggest that the vast majority of imported enums should source-break all 
> switches in Swift 5. Why is that acceptable, but making Swift enums 
> source-breaking unacceptable?

Good point! I think it's acceptable because people were unlikely to be 
exhaustively switching over imported enums anyway, on the grounds that most of 
them aren't just non-exhaustive but actually don't make any sense to switch 
over in the first place. On the other hand, breaking Swift enums (when not 
migrated) is something that could cause pain for a while, rather than just 
being a one-time "go fix all the compiler issues". But you all are convincing 
me it's not as big a deal as I thought.


> 
> I suspect that, in practice, `public` enums tend to fall into two categories:
> 
>   1. "Data enums" which represent important data that happens to consist 
> of a set of alternatives. Outside users will frequently need to switch over 
> these, but they are not very likely to evolve or have private cases.
> 
>   2. "Mode enums" which tweak the behavior of an API. These are very 
> likely to evolve or have private cases, but outside users are not very likely 
> to need to switch over them.
> 
> An example of a data enum would be, as you mentioned, `NSComparisonResult`. 
> People really *do* need to be able to test against it, but barring some 
> fundamental break in the nature of reality, it will only ever have those 
> three cases. So it's fine to make it exhaustive.
> 
> An example of a mode enum would be `UIViewAnimationCurve`, which tells UIKit 
> how to ease an animation. I chose that example because I actually traced a 
> bug just last week to my mistaken impression that this enum had no private 
> cases. I was mapping values of this type to their corresponding 
> `UIViewAnimationOptions` values; because there were private cases, this was 
> Objective-C code, and I didn't include sufficiently aggressive assertions, I 
> ended up reading garbage data from memory. But while debugging this, it 
> struck me that this was actually *really weird* code. How often do you, as a 
> developer outside UIKit, need to interpret the value of a type like 
> `UIViewAnimationCurve`? If the compiler suddenly changed the exhaustiveness 
> behavior of `UIViewAnimationCurve`, probably less than 1% of apps would even 
> notice—and the affected code would probably have latent bugs!
> 
> Here's my point: Suddenly treating a mode enum as non-exhaustive is 
> *technically* source-breaking, but *people aren't doing things to them that 
> would break*. It is only the data enums that would actually experience source 
> breakage, and we both seem to agree those a

Re: [swift-evolution] Enums and Source Compatibility

2017-09-12 Thread Jordan Rose via swift-evolution
Sorry, I got distracted by other tasks! Both the discussion here and within 
Apple has moved towards making "non-exhaustive" the default, which, to be 
honest, I too think is the best design. I'll update the proposal today to 
reflect that, though I still want to keep both the "nonexhaustive" and 
"exhaustive" keywords for Swift 4 compatibility for now (or whatever we end up 
naming them). The compatibility design is a little less ambitious than Brent's; 
as currently proposed, Swift 4 mode continues to default to 'exhaustive' all 
the time, even in the actual Swift 5 release.

I still want to respond to Brent's points directly, but I think you and 
Vladimir have done a good job discussing them already. I'll send out the 
updated proposal tomorrow, after I have a little more time to think about 
#invalid.

Thanks for putting time into this!
Jordan


> On Sep 9, 2017, at 17:34, Rod Brown  wrote:
> 
> Jordan,
> 
> Do you have any other thoughts about the ongoing discussion here, especially 
> regarding Chris’ comments? As you’re the one pushing this forward, I’d really 
> like to know what your thoughts are regarding this?
> 
> - Rod

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-12 Thread Jordan Rose via swift-evolution
I don't think RawRepresentable really has anything to do with this. Enums with 
payloads often don't have any particular reasonable raw values.

We could certainly spell non-exhaustive enums as "enum struct", but I don't 
think that'll be more obvious to other people than any of the names that have 
already been proposed.

Jordan


> On Sep 6, 2017, at 11:05, Jose Cheyo Jimenez <ch...@masters3d.com> wrote:
> 
> Here is an alternative view. I've been thinking about this and I feel that 
> instead of adding this to an enum why not make RawRepresentable structs a 
> swift construct. 
> 
> You could declare it like this:
> 
> enum struct {
>case a, b, c
> }
> 
> This would be a struct that acts like an enum but it is open like a 
> RawRepresentable but using the enum case sugar. 
> 
> On Sep 5, 2017, at 5:37 PM, Jordan Rose via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> 
>> It's in the "Alternatives Considered" section. :-) That was my desired 
>> design when we started, but feedback convinced me that the break from Swift 
>> 4 mode would be too drastic. The same valid code would have a different 
>> meaning whether you were writing Swift 4 or Swift 5.
>> 
>> Jordan
>> 
>> 
>>> On Sep 5, 2017, at 17:30, Rod Brown <rodney.bro...@icloud.com 
>>> <mailto:rodney.bro...@icloud.com>> wrote:
>>> 
>>> Hi Jordan,
>>> 
>>> I’m not sure how much bearing on this my comment will have.
>>> 
>>> Have you considered having only “exhaustive” as a keyword, and make the 
>>> default non-exhaustive? It seems that “exhaustive" would be the rarer case, 
>>> as it promises a lot more about compatibility (much like there is no such 
>>> thing as “non-final”). Also, non exhaustive seems a massive mouthful 
>>> despite it probably being the correct term.
>>> 
>>> - Rod
>>> 
>>>> On 6 Sep 2017, at 10:19 am, Jordan Rose <jordan_r...@apple.com 
>>>> <mailto:jordan_r...@apple.com>> wrote:
>>>> 
>>>> I've taken everyone's feedback into consideration and written this up as a 
>>>> proposal: 
>>>> https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
>>>>  
>>>> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md>.
>>>>  The next step is working on an implementation, but if people have further 
>>>> pre-review comments I'd be happy to hear them.
>>>> 
>>>> Jordan
>> 
>> ___
>> swift-evolution mailing list
>> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>> <https://lists.swift.org/mailman/listinfo/swift-evolution>

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


Re: [swift-evolution] [swift-evolution-announce] [Review] SE-0184: Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size

2017-09-07 Thread Jordan Rose via swift-evolution


> On Sep 7, 2017, at 17:46, Andrew Trick  wrote:
> 
> 
>> On Sep 7, 2017, at 5:40 PM, Joe Groff  wrote:
>> 
>> 
>>> 
 But then given that, I don't understand why the 'capacity' parameter is 
 necessary. Under what circumstances would it actually be faster than 
 "just" calling malloc_size?
>>> 
>>> The runtime may need to hash the address or traverse a lookup table to find 
>>> out which allocation pool the block resides in. Now, that’s only if some 
>>> platform ditches full malloc compatibility for user allocations, so I’m not 
>>> sure how realistic it is.
>> 
>> It seems to me that you could still provide malloc/free compatibility with a 
>> zone that had to do a relatively expensive traversal on free() to recover 
>> the pool the memory came from; malloc/free just wouldn't be the ideal 
>> interface in that situation.
>> 
>> -Joe
> 
> Joe is right, and I just learned how amazing malloc zones are.

As long as you support multiple allocators (or hide everything behind 
malloc/free), there's already a cost of malloc_zone_from_ptr or equivalent. 
Without seeing a concrete use case, I wouldn't want to stay with the 
harder-to-use API in UnsafePointer itself. It might be a feature of a 
particular allocator that you need to keep the capacity around, but it isn't 
something generally true about Swift's memory model, and probably never will be.

(Interesting reference points: malloc/malloc.h and the implementation of malloc 
on macOS 

 - search for "free(void *ptr)".)

Jordan

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


Re: [swift-evolution] [swift-evolution-announce] [Review] SE-0184: Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size

2017-09-07 Thread Jordan Rose via swift-evolution


> On Sep 7, 2017, at 17:09, Andrew Trick  wrote:
> 
> 
>> On Sep 7, 2017, at 2:29 PM, Jordan Rose > > wrote:
>> 
>>> 
>>> We do want the Swift memory model to be consistent with the reality that on 
>>> most platforms, we need the runtime to track block size.
>> 
>> I don't know where this comes from. If you don't need to be 
>> malloc-compatible, you don't strictly "need" this information. (It would 
>> break tools like 'leaks', though.)
> 
> There are two distinct but related issues (1) malloc compatibility (2) 
> malloc/free like functionality. I know developers sometimes expect or want 
> #2. Realistically, we will always want the runtime to provide malloc_size, 
> even if it’s not super fast, so we’re not giving up anything long term by 
> providing #2. The fact the #1 is also a likely goal on major platforms just 
> reinforces that position.

I don't understand why "realistically, we will always want the runtime to 
provide malloc_size". Could you explain why?

But then given that, I don't understand why the 'capacity' parameter is 
necessary. Under what circumstances would it actually be faster than "just" 
calling malloc_size?

Jordan

> 
>>> If our memory model states that the runtime tracks capacity of manually 
>>> allocated blocks, then the deallocation capacity should be optional to 
>>> reflect that.
>>> 
>>> Still waiting to hear any arguments that something about that memory model 
>>> is bad.
>> 
>> I don't see an advantage to having the parameter if we're going to promise 
>> that it's always present. It is additional complexity for very little win in 
>> a generic interface. If someone wants to write a special allocation entry 
>> point that actually makes use of this, they can do so, but it shouldn't live 
>> on UnsafePointer.
>> 
>> (We also have nothing that prevents you from doing `(ptr+1).deallocate()`, 
>> but, well, "unsafe".)
> 
> I also don’t see any usability advantage in providing the extra argument for 
> anyone directly using UnsafePointer, which is why I initially objected to Joe 
> Groff’s request. Then I realized I care less about usability and potential 
> confusion than I care that UnsafePointer API can be viewed is a specification 
> of the Swift's basic memory management functionality. We want to communicate 
> that data structures doing manual allocation/deallocation should provide the 
> allocated capacity during deallocation if it is available. The runtime could 
> make good use of that. In particular, I want UnsafeBufferPointer’s 
> deallocate() to be able to call UnsafePointer.deallocate(allocatedCapacity: 
> buffer.count), rather than implementing it in terms of Builtins.
> 
> Incidentally, FWIW, I think compiled code should continue to be required to 
> pass the capacity to the runtime during deallocation. That way, any changes 
> in the runtime implementation of deallocation, particularly extra address 
> checks, are isolated to the standard library.

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


Re: [swift-evolution] [swift-evolution-announce] [Review] SE-0184: Unsafe[Mutable][Raw][Buffer]Pointer: add missing methods, adjust existing labels for clarity, and remove deallocation size

2017-09-07 Thread Jordan Rose via swift-evolution


> On Sep 7, 2017, at 13:02, Andrew Trick  wrote:
> 
> 
>> On Sep 7, 2017, at 11:39 AM, Jordan Rose > > wrote:
>> 
>>> This discussion needs to be grounded by reiterating role of the API. 
>>> UnsafePointer specifies the memory model without extraneous functionality 
>>> or convenience.
>>> 
>>> The UnsafePointer.deallocate() API *is not*:
>>> 
>>> - a common, expected, or encouraged way to deallocate
>>> 
>>> - the simplest, safest, or most convenient way to deallocate
>>> 
>>> - necessarilly the most optimal path for deallocation
>> 
>> I don't think this is correct. UnsafePointer.deallocate is the API you must 
>> use to deallocate memory allocated with UnsafePointer.allocate.
> 
> No, all of Swift’s APIs for manual allocation/deallocation need to be 
> compatible. UnsafeBufferPointer is highly preferable to UnsafePointer today. 
> In the future we will have a safe API for manual allocation, still based on 
> the underlying model specified by UnsafePointer.
> 
> UnsafePointer.allocate() is *not*
> 
> - a common, expected, or encouraged way to allocate
> 
> - the simplest, safest, or most convenient way to allocate
> 
> - necessarily the most optimal path for allocation
> 
> Even though high-level APIs are specified in terms of this model, they can be 
> implemented via their own fast-paths.

That's all fine; I'll rephrase to say that if I allocated with 
UnsafeMutablePointer.allocate, I'll probably deallocate with 
Unsafe[Mutable]Pointer.deallocate. They're not independent.


> 
>>  Myquestion is whether it's acceptable to break all the people who didn't 
>> know this and are using it to deallocate memory allocated with malloc or new 
>> on Apple platforms. It sounds like the answer to that is "no, we want to be 
>> malloc-compatible", and therefore the 'capacity' parameter isn't currently 
>> serving a purpose today. We will always need to check if the memory is 
>> actually in the Swift pool before even believing the 'capacity' parameter.
> 
> We don’t need to claim that manually allocated Swift memory is malloc 
> compatible on every platform that happens to have malloc.

I suspect that we cannot revoke that from existing platforms.

> We do want the Swift memory model to be consistent with the reality that on 
> most platforms, we need the runtime to track block size.

I don't know where this comes from. If you don't need to be malloc-compatible, 
you don't strictly "need" this information. (It would break tools like 'leaks', 
though.)


> 
>> (It is definitely true that the intent was for this to be the allocation 
>> capacity, and I'm surprised you interpreted it as supporting partial 
>> deallocation. But we probably can't fix that at this point.)
> 
> I never misinterpreted the meaning of the API, but apparently multiple people 
> on the evolution list did. Regardless, that is not valid reason for changing 
> the API. It’s only a valid reason for improving documentation and encouraging 
> the use of safer APIs.

That "you" was addressed to Kelvin (Taylor), but if we really think people are 
mixing malloc/free and UnsafePointer APIs today, we should not break that on 
our existing platforms. It's unfortunate, but we're on Swift 4 already. It is 
not worth the cost.


> If our memory model states that the runtime tracks capacity of manually 
> allocated blocks, then the deallocation capacity should be optional to 
> reflect that.
> 
> Still waiting to hear any arguments that something about that memory model is 
> bad.

I don't see an advantage to having the parameter if we're going to promise that 
it's always present. It is additional complexity for very little win in a 
generic interface. If someone wants to write a special allocation entry point 
that actually makes use of this, they can do so, but it shouldn't live on 
UnsafePointer.

(We also have nothing that prevents you from doing `(ptr+1).deallocate()`, but, 
well, "unsafe".)

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-05 Thread Jordan Rose via swift-evolution
It's in the "Alternatives Considered" section. :-) That was my desired design 
when we started, but feedback convinced me that the break from Swift 4 mode 
would be too drastic. The same valid code would have a different meaning 
whether you were writing Swift 4 or Swift 5.

Jordan


> On Sep 5, 2017, at 17:30, Rod Brown  wrote:
> 
> Hi Jordan,
> 
> I’m not sure how much bearing on this my comment will have.
> 
> Have you considered having only “exhaustive” as a keyword, and make the 
> default non-exhaustive? It seems that “exhaustive" would be the rarer case, 
> as it promises a lot more about compatibility (much like there is no such 
> thing as “non-final”). Also, non exhaustive seems a massive mouthful despite 
> it probably being the correct term.
> 
> - Rod
> 
>> On 6 Sep 2017, at 10:19 am, Jordan Rose > > wrote:
>> 
>> I've taken everyone's feedback into consideration and written this up as a 
>> proposal: 
>> https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
>>  
>> .
>>  The next step is working on an implementation, but if people have further 
>> pre-review comments I'd be happy to hear them.
>> 
>> Jordan

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


Re: [swift-evolution] Enums and Source Compatibility

2017-09-05 Thread Jordan Rose via swift-evolution
I've taken everyone's feedback into consideration and written this up as a 
proposal: 
https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/-non-exhaustive-enums.md
 
.
 The next step is working on an implementation, but if people have further 
pre-review comments I'd be happy to hear them.

Jordan


> On Aug 8, 2017, at 15:27, Jordan Rose  wrote:
> 
> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to an 
> issue that's been around for a while: the source compatibility of enums. 
> Today, it's an error to switch over an enum without handling all the cases, 
> but this breaks down in a number of ways:
> 
> - A C enum may have "private cases" that aren't defined inside the original 
> enum declaration, and there's no way to detect these in a switch without 
> dropping down to the rawValue.
> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an 
> imported enum never produces 'nil', because who knows how anyone's using C 
> enums anyway?
> - Adding a new case to a Swift enum in a library breaks any client code that 
> was trying to switch over it.
> 
> (This list might sound familiar, and that's because it's from a message of 
> mine on a thread started by Matthew Johnson back in February called "[Pitch] 
> consistent public access modifiers". Most of the rest of this email is going 
> to go the same way, because we still need to make progress here.)
> 
> At the same time, we really like our exhaustive switches, especially over 
> enums we define ourselves. And there's a performance side to this whole thing 
> too; if all cases of an enum are known, it can be passed around much more 
> efficiently than if it might suddenly grow a new case containing a struct 
> with 5000 Strings in it.
> 
> 
> Behavior
> 
> I think there's certain behavior that is probably not terribly controversial:
> 
> - When enums are imported from Apple frameworks, they should always require a 
> default case, except for a few exceptions like NSRectEdge. (It's Apple's job 
> to handle this and get it right, but if we get it wrong with an imported enum 
> there's still the workaround of dropping down to the raw value.)
> - When I define Swift enums in the current framework, there's obviously no 
> compatibility issues; we should allow exhaustive switches.
> 
> Everything else falls somewhere in the middle, both for enums defined in 
> Objective-C:
> 
> - If I define an Objective-C enum in the current framework, should it allow 
> exhaustive switching, because there are no compatibility issues, or not, 
> because there could still be private cases defined in a .m file?
> - If there's an Objective-C enum in another framework (that I built locally 
> with Xcode, Carthage, CocoaPods, SwiftPM, etc.), should it allow exhaustive 
> switching, because there are no binary compatibility issues, or not, because 
> there may be source compatibility issues? We'd really like adding a new enum 
> case to not be a breaking change even at the source level.
> - If there's an Objective-C enum coming in through a bridging header, should 
> it allow exhaustive switching, because I might have defined it myself, or 
> not, because it might be non-modular content I've used the bridging header to 
> import?
> 
> And in Swift:
> 
> - If there's a Swift enum in another framework I built locally, should it 
> allow exhaustive switching, because there are no binary compatibility issues, 
> or not, because there may be source compatibility issues? Again, we'd really 
> like adding a new enum case to not be a breaking change even at the source 
> level.
> 
> Let's now flip this to the other side of the equation. I've been talking 
> about us disallowing exhaustive switching, i.e. "if the enum might grow new 
> cases you must have a 'default' in a switch". In previous (in-person) 
> discussions about this feature, it's been pointed out that the code in an 
> otherwise-fully-covered switch is, by definition, unreachable, and therefore 
> untestable. This also isn't a desirable situation to be in, but it's 
> mitigated somewhat by the fact that there probably aren't many framework 
> enums you should exhaustively switch over anyway. (Think about Apple's 
> frameworks again.) I don't have a great answer, though.
> 
> For people who like exhaustive switches, we thought about adding a new kind 
> of 'default'—let's call it 'unknownCase' just to be able to talk about it. 
> This lets you get warnings when you update to a new SDK, but is even more 
> likely to be untested code. We didn't think this was worth the complexity.
> 
> 
> Terminology
> 
> The "Library Evolution 
> " doc (mostly written 
> by me) originally called these "open" and "closed" enums ("requires a 
> default" and "allows exhaustive switching", 

Re: [swift-evolution] Why you can't make someone else's class Decodable: a long-winded explanation of 'required' initializers

2017-09-05 Thread Jordan Rose via swift-evolution



> On Sep 2, 2017, at 00:30, Fabian Ehrentraud <fabian.ehrentr...@willhaben.at> 
> wrote:
> 
> 
> Am 03.08.2017 um 02:09 schrieb Jordan Rose via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
> 
>> David Hart recently asked on Twitter 
>> <https://twitter.com/dhartbit/status/891766239340748800> if there was a good 
>> way to add Decodable support to somebody else's class. The short answer is 
>> "no, because you don't control all the subclasses", but David already 
>> understood that and wanted to know if there was anything working to mitigate 
>> the problem. So I decided to write up a long email about it instead. (Well, 
>> actually I decided to write a short email and then failed at doing so.)
>> 
>> The Problem
>> 
>> You can add Decodable to someone else's struct today with no problems:
>> 
>> extension Point: Decodable {
>>   enum CodingKeys: String, CodingKey {
>> case x
>> case y
>>   }
>>   public init(from decoder: Decoder) throws {
>> let container = try decoder.container(keyedBy: CodingKeys.self)
>> let x = try container.decode(Double.self, forKey: .x)
>> let y = try container.decode(Double.self, forKey: .y)
>> self.init(x: x, y: y)
>>   }
>> }
>> 
>> But if Point is a (non-final) class, then this gives you a pile of errors:
>> 
>> - init(from:) needs to be 'required' to satisfy a protocol requirement. 
>> 'required' means the initializer can be invoked dynamically on subclasses. 
>> Why is this important? Because someone might write code like this:
>> 
>> func decodeMe() -> Result {
>>   let decoder = getDecoderFromSomewhere()
>>   return Result(from: decoder)
>> }
>> let specialPoint: VerySpecialSubclassOfPoint = decodeMe()
>> 
>> …and the compiler can't stop them, because VerySpecialSubclassOfPoint is a 
>> Point, and Point is Decodable, and therefore VerySpecialSubclassOfPoint is 
>> Decodable. A bit more on this later, but for now let's say that's a sensible 
>> requirement.
>> 
>> - init(from:) also has to be a 'convenience' initializer. That one makes 
>> sense too—if you're outside the module, you can't necessarily see private 
>> properties, and so of course you'll have to call another initializer that 
>> can.
>> 
>> But once it's marked 'convenience' and 'required' we get "'required' 
>> initializer must be declared directly in class 'Point' (not in an 
>> extension)", and that defeats the whole purpose. Why this restriction?
>> 
>> 
>> The Semantic Reason
>> 
>> The initializer is 'required', right? So all subclasses need to have access 
>> to it. But the implementation we provided here might not make sense for all 
>> subclasses—what if VerySpecialSubclassOfPoint doesn't have an 'init(x:y:)' 
>> initializer? Normally, the compiler checks for this situation and makes the 
>> subclass reimplement the 'required' initializer…but that only works if the 
>> 'required' initializers are all known up front. So it can't allow this new 
>> 'required' initializer to go by, because someone might try to call it 
>> dynamically on a subclass. Here's a dynamic version of the code from above:
>> 
>> func decodeDynamic(_ pointType: Point.Type) -> Point {
>>   let decoder = getDecoderFromSomewhere()
>>   return pointType.init(from: decoder)
>> }
>> let specialPoint = decodeDynamic(VerySpecialSubclassOfPoint.self)
>> 
>> 
>> The Implementation Reason
>> 
>> 'required' initializers are like methods: they may require dynamic dispatch. 
>> That means that they get an entry in the class's dynamic dispatch table, 
>> commonly known as its vtable. Unlike Objective-C method tables, vtables 
>> aren't set up to have entries arbitrarily added at run time.
>> 
>> (Aside: This is one of the reasons why non-@objc methods in Swift extensions 
>> can't be overridden; if we ever lift that restriction, it'll be by using a 
>> separate table and a form of dispatch similar to objc_msgSend. I sent a 
>> proposal to swift-evolution about this last year but there wasn't much 
>> interest.)
>> 
>> 
>> The Workaround
>> 
>> Today's answer isn't wonderful, but it does work: write a wrapper struct 
>> that conforms to Decodable instead:
>> 
>> struct DecodedPoint: Decodable {
>>   var value: Point
>>   enum CodingKeys: String, CodingKey {
>> case x
>> case y
>>   }
>>   public init(from deco

Re: [swift-evolution] Enums and Source Compatibility

2017-08-11 Thread Jordan Rose via swift-evolution


> On Aug 11, 2017, at 02:59, Vladimir.S  wrote:
> 
> On 11.08.2017 2:37, Jordan Rose wrote:
>> Both you and Vladimir are bringing up this point, with Vladimir explicitly 
>> suggesting a "future" case that's different from "default". Again, the 
>> pushback we get here is that the "future" case is untestable…but maybe 
>> that's still an option worth having. 
> 
> I wonder, how the 'default' in exhaustive switch on open enum is testable?
> 
> I mean, let's say we have such enum in one of the frameworks:
> 
> open enum MyOpenEnum {
>  case one
>  case two
> }
> 
> , then in our code:
> 
> switch myOpenEnumInstance {
>  case .one : ...
>  case .two : ...
>  default : ... // how this can be tested?
> }
> 
> I just strongly feel that be able to keep switch exhaustive at the moment of 
> compilation - is critical requirement in some cases, when it is very 
> important to not forget to process some cases. With just 'default' in switch 
> for open enum - we are loosing this compiler's help. This is why 'future' 
> case is required for open enums.
> 
> Also, if I understand correctly, we are going to have most of all 
> extern(imported) enums as 'open'. So, we are loosing the feature to receive a 
> help for exhaustive switch from compiler for most of such enums.

That's true, but I don't know what to do about it. My hypothesis, again, is 
that this won't be common in practice; so far Charlie's been the only one to 
provide a real-world example of when this is useful.


> Moreover, shouldn't we just say that enums, that we have no Swift sources for 
> at the moment of compilation - should *always* be treated as 'open'? If we 
> compile our 'switch' together with the source of switched enum - such enum 
> can not be changed in the future.
> But, if enum is coming from other framework - we have no any control over it, 
> and even author of framework IMO can't be sure in most cases that enum will 
> not be extended in future, and probably we even should not ask author of 
> framework to consider its enum closed, as most likely one can't foresee for 
> sure.
> 
> Wouldn't this be a simpler and more robust model to think about enums in 
> Swift?
> So you know, that *any* enum coming from framework(not from source) - can be 
> changed in future, and so you have to process this case explicitly. In this 
> case we don't need to mark enum as 'open' or 'closed' at all, but for some 
> rare cases, when author of framework *really sure* enum can not be changed in 
> future(and future change in enum will break all the code depending on it), we 
> can introduce some 'final' marker(or @exhaustive directive) to help 
> compiler's optimizations.

Again, some enums really do want to be exhaustive: Foundation.ComparisonResult, 
Swift.Optional, and in general anything the framework owner really does want 
people to exhaustively switch over. These aren't just optimization concerns 
because they affect how people are expected to use the type. I think this all 
just means that you're on the side that "open" should be the default.


> 
> Btw, is open enum is allowed to change the type of associated value for some 
> cases or even enum's raw type? I.e. what changes in open enum will lead to 
> crash in our code and which will just be processed in 'default'/'future' 
> block in switch?

Nope, that's not planned to be allowed. That would break source compatibility 
outside of just switch—it would also affect `if case` as well as the creation 
of an enum with that case.

The last time I thought about this, I came up with this list of things we want 
to allow in "open" enums:

• Adding a new case.
• Reordering existing cases is a "binary-compatible source-breaking 
change". In particular, if an enum is RawRepresentable, changing the raw 
representations of cases may break existing clients who use them for 
serialization.
• Adding a raw type to an enum that does not have one.
• Removing a non-public, non-versioned case (if we ever have such a 
thing).
• Adding any other members.
• Removing any non-public, non-versioned members.
• Adding a new protocol conformance (with proper annotations).
• Removing conformances to non-public protocols.

We're now questioning whether reordering should be allowed at all for 
implementation reasons, but other than that this list should still be accurate.

> 
> Vladimir. (P.S. Sorry for long reply)

Thanks for thinking about this in detail; better to get the problems out in the 
open now before I write up a formal proposal!

Jordan

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


Re: [swift-evolution] Enums and Source Compatibility - defaults

2017-08-10 Thread Jordan Rose via swift-evolution


> On Aug 10, 2017, at 13:00, David Hart  wrote:
> 
> 
> 
> On 10 Aug 2017, at 19:19, Jordan Rose  > wrote:
> 
>> 
>> 
>>> On Aug 9, 2017, at 22:46, David Hart >> > wrote:
>>> 
>>> 
 On 10 Aug 2017, at 02:42, Jordan Rose > wrote:
 
 :-) As you've all noted, there are some conflicting concerns for the 
 default:
 
 - Source compatibility: the existing behavior for an unannotated enum is 
 "closed".
 - Intuition: if you show someone an enum without an explicit annotation, 
 they'll probably expect they can switch over it. (I'm going to say this is 
 why Zach calls it a "sensible default".)
 - Consistency: switches on an enum in the same module can always be 
 exhaustive, so having it be different across modules is a bit annoying. 
 (But 'public' already acts like this.)
 
 vs.
 
 - Library evolution: the default should promise less, so that you have the 
 opportunity to change it.
 - Flexibility: you can emulate an exhaustive switch with a non-exhaustive 
 switch using fatalError, but not the other way around.
 
 All of this is why I suggested it be an explicit annotation in either 
 direction, but Matthew brought up the "keyword soup" problem—if you have 
 to write (say) "public finite enum" and "public infinite enum", but would 
 never write "private finite enum" or "private infinite enum", something is 
 redundant here. Still, I'm uncomfortable with the default case being the 
 one that constrains library authors, so at least for binary frameworks 
 (those compiled "with resilience") I would want that to be explicit. That 
 brings us to one more concern: how different should binary frameworks be 
 from source frameworks?
>>> 
>>> In terms of intuition and consistency, I think we should really try to 
>>> learn from the simplicity of public/open:
>>> 
>>> * When internal, classes are sub-classable by default for convenience, but 
>>> can be closed with the final keyword
>>> * When public, classes are closed to sub-classing for safety, but can be 
>>> opened up with the open keyword (which implies public).
>>> 
>>> If we try to mirror this behaviour (the keywords are just suggestions, not 
>>> important):
>>> 
>>> * When internal, enums are exhaustive by default for convenience, but can 
>>> be opened-up with the partial keyword
>>> * When public, enums are non-exhaustive by default for safety, but can be 
>>> made exhaustive with the exhaustive keyword (which implies public).
>> 
>> This is not a correct understanding of the internal/public distinction for 
>> classes, though. From inside a module, a public-but-not-open class is still 
>> subclassable, and similarly a public-but-not-"closed" enum will still be 
>> exhaustively switched. You don't have to worry about your own module 
>> changing out from under you.
> 
> Correct. Thanks for the clarification! But you still agree with the argument, 
> right? Convenience for same module enums (exhaustive by default), safety for 
> clients of the module (not-exhaustive when public from outside the module), 
> with the option to be explicit?

That's the "library evolution" criterion above, yes. But the behavior of enums 
inside the module doesn't need to affect what we do across modules.

> And what do you think of the idea of having the « exhaustiveness » modifier 
> imply public, like open does?

I'm a little less sure about that. It does help with the keyword soup problem, 
but it's also not as obviously about access as "open" was, and "open" can also 
appear where other access modifiers can appear (on methods, properties, and 
subscripts).

Jordan

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


Re: [swift-evolution] Enums and Source Compatibility

2017-08-10 Thread Jordan Rose via swift-evolution
Both you and Vladimir are bringing up this point, with Vladimir explicitly 
suggesting a "future" case that's different from "default". Again, the pushback 
we get here is that the "future" case is untestable…but maybe that's still an 
option worth having. (At the very least, it's worth recording in any eventual 
proposal why we don't have it, and it could be added later if it turns out that 
was wrong.)

Thank you both for pushing on it.

Jordan


> On Aug 9, 2017, at 21:55, Charlie Monroe <char...@charliemonroe.net> wrote:
> 
> Hi Jordan,
> 
> let's say I'm writing my custom number formatter and I switch over 
> NSNumberFormatterStyle (NumberFormatter.Style in Swift) - the question always 
> is what to do with the default case - it's a value that I am not 
> programmatically counting with. I would personally just put in fatalError 
> with a description that it was passed an unhandled style. Listing all enums 
> in Foundation, I can see using most of them this way.
> 
> I personally have most of my switches exhaustive, mainly for the sake of 
> being warned/error'ed when a new case is introduced - I've just done a quick 
> search through my projects and I use default: usually for switching over 
> non-enums (strings, object matching, ints, ...).
> 
> Maybe I'm in the minority here... Seemed like a good practice to me - usually 
> the enum doesn't have but a few items on the list and you usually don't 
> handle just 1-2 cases in your switch, which makes the default label save you 
> 1-2 lines of code that can save you from unnnecessarily crashing during 
> runtime...
> 
>> On Aug 10, 2017, at 1:57 AM, Jordan Rose <jordan_r...@apple.com 
>> <mailto:jordan_r...@apple.com>> wrote:
>> 
>> Hi, Charlie. This is fair—if you're switching over an open enum at all, 
>> presumably you have a reason for doing so and therefore might want to handle 
>> all known cases every time you update your SDK. However, I think in practice 
>> that's going to be rare—do you have examples of exhaustive switches on SDK 
>> enums that exist in your own app?
>> 
>> (There's an additional piece about how to handle cases with different 
>> availability—there's nowhere obvious to write the #available.)
>> 
>> I suspect marking SDK enums "closed" will be much easier than nullability, 
>> simply because there are so few of them. Here's some data to that effect: 
>> out of all  60 or so NS_ENUMs in Foundation, only 6 of them are ones I would 
>> definitely mark "closed":
>> 
>> - NSComparisonResult
>> - NSKeyValueChange / NSKeyValueSetMutationKind
>> - NSRectEdge
>> - NSURLRelationship
>> - maybe NSCalculationError
>> 
>> There are a few more, like NSURLHandleStatus, where I could see someone 
>> wanting to exhaustively switch as well, but the main point is that there is 
>> a clear default for public enums, at least in Objective-C, and that even in 
>> a large framework it's not too hard to look at all of them.
>> 
>> (Note that NSComparisonResult is technically not part of Foundation; it 
>> lives in the ObjectiveC module as /usr/include/objc/NSObject.h.)
>> 
>> Jordan
>> 
>> 
>>> On Aug 8, 2017, at 21:53, Charlie Monroe <char...@charliemonroe.net 
>>> <mailto:char...@charliemonroe.net>> wrote:
>>> 
>>> While I agree with the entire idea and would actually use behavior like 
>>> this in a few instances, I feel that in most cases, you would simply put 
>>> 
>>> default:
>>> fatalError()
>>> 
>>> The huge downside of this is that you no longer get warned by the compiler 
>>> that you are missing a case that was added - a common thing I personally do 
>>> (and I have a feeling I'm not alone) - add an enum case, build the app, see 
>>> what broke and fix it - as you get warnings/errors about the switch not 
>>> being exhaustive. You find this out during runtime (if you're lucky), 
>>> otherwise your end user.
>>> 
>>> As you've noted all enums from ObjC would need to be marked with an 
>>> annotation marking if they are closed - which given the way nullability is 
>>> still missing in many frameworks out there, I think would take years.
>>> 
>>> I'd personally expand this proposal by introducing switch! (with the 
>>> exclamation mark) which would allow to treat open enums as closed. Example:
>>> 
>>> // Imported from ObjC
>>> open enum NSAlert.Style { ... }
>>> 
>>> switch! alert.style {
>>> case .warning:
>>> // ...
>>

Re: [swift-evolution] Enums and Source Compatibility - defaults

2017-08-10 Thread Jordan Rose via swift-evolution


> On Aug 9, 2017, at 20:00, Zach Waldowski via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> On Wed, Aug 9, 2017, at 08:49 PM, Jordan Rose via swift-evolution wrote:
>> Ah, I forgot to mention: per my response to Charlie, the vast majority of 
>> enums in ObjC Foundation (80-90%) seem to be "open". I should have put that 
>> in the "open" side of the list. I'm not convinced that "APIs are designed 
>> differently in Swift" to the extent where you'd actually expect most enums 
>> to be closed; rather, I suspect most Swift API designers haven't been 
>> thinking too hard about exhaustive switches, and certainly not about binary 
>> compatibility, which becomes relevant at least for Apple once Swift 
>> libraries become part of the SDK.
> 
> Let me expound a bit. I was erroneously including string enums in my 
> supposition that APIs are designed differently, but overall I still stand 
> behind it.
> 
> Foundation is designed for paper over lots of different computing concepts 
> and underlying libraries with similar-ish APIs — in a good way! From the eyes 
> of an API consumer, configuring something like a formatter to behave how they 
> want is basically a write-only proposition. This makes simple enum cases a 
> great design choice for hiding swaths of different underlying implementations 
> (ex: NSDateFormatterShortStyle having a different meaning per-locale.)
> 
> Say we split up Foundation's enums roughly into categories. You have your 
> policies (NSURLCacheStoragePolicy, NSDateFormatterStyle), states/answers 
> (NSURLSessionTaskState, NSQualityOfService), and, well, lists 
> (NSSearchPathDirectory, NSStringEncoding). Given those, I’d say protocols, 
> generics, and zero-cost abstractions are frequently a better choice (but not 
> always). For instance, IMHO a closed protocol better models commonly-used 
> styles.
> 
> I know you ask to only consider open/closed enums in isolation to make it 
> most likely to succeed, but there’s the frequent complaint that Swift has 
> many ways to accomplish the same thing; I think resilience will land best 
> with the community if it has a few really solid throughlines with the rest of 
> the language, open/closed I hope being among them.

For what it's worth, the rest of "resilience" mostly doesn't have semantic 
effects at all. Enums are special because of the source-affecting aspects. 
(That'll certainly make it weird to talk about the rest of resilience; I 
suspect most of the group won't be interested.)

Thanks for listing this out, Zach. I don't think I agree with the conclusion 
that enums should and will be used less in Swift, but I'm not the only one 
whose opinion matters on this.

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


Re: [swift-evolution] [Review] SE-0185 - Synthesizing Equatable and Hashable conformance

2017-08-10 Thread Jordan Rose via swift-evolution


> On Aug 10, 2017, at 15:34, Tony Allevato  wrote:
> 
> Do you mean something like this, then?
> 
> ```
> struct Foo: Equatable {
>   let x: Int
> }
> 
> func test(_ lhs: T, _ rhs: T) -> Bool {
>   return lhs == rhs
> }
> 
> extension Foo {
>   static func == (lhs: Foo, rhs: Foo) -> Bool {
> return lhs.x % 2 == rhs.x % 2
>   }
> }
> 
> print(test(Foo(x: 5), Foo(x: 7)))  // true
> ```
> 
> That seems to work.

Ah, yes, this works in a source file. It may even work in the REPL, if the REPL 
delays evaluating input until it sees an actual statement. However, the 
original REPL transcript you pasted in would not have worked.

> 
> I just tested the Annoying example as well and yes, the version from the 
> Annoying extension is what gets called in my implementation.
> 
> I agree with you that that seems like the correct behavior—the manually 
> written version takes precedence over the synthesized version, even though it 
> comes from a different protocol extension; synthesized versions should always 
> be the last resort because they're not controlled by the user, correct?
> 
> This also seems consistent with the way regular methods are resolved if we 
> take synthesis completely out of the equation:
> 
> ```
> protocol Fooquatable {
>   static func foo(lhs: Self, rhs: Self) -> Bool
> }
> 
> protocol Annoying {}
> extension Annoying {
>   static func foo(lhs: Self, rhs: Self) -> Bool {
> print("annoying")
> return true
>   }
> }
> struct Foo: Fooquatable, Annoying {
>   let x: Int
> }
> 
> func foo(_ lhs: T, _ rhs: T) -> Bool {
>   return T.foo(lhs: lhs, rhs: rhs)
> }
> 
> print(foo(Foo(x: 5), Foo(x: 6)))  // annoying, true
> ```
> 
> Does this seems reasonable? (Assuming I'm testing the right thing. :)

Yep, that's why I think this is the right behavior as well. It does mean that 
this synthesis doesn't quite behave like a usual default implementation, 
because that would make this code ambiguous. If we really wanted it to match 
that behavior we'd have to rank it like a kind of protocol extension member. I 
think it's okay not to do that, though.

Jordan

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


Re: [swift-evolution] [Review] SE-0185 - Synthesizing Equatable and Hashable conformance

2017-08-10 Thread Jordan Rose via swift-evolution


> On Aug 10, 2017, at 14:48, Tony Allevato  wrote:
> 
> On Thu, Aug 10, 2017 at 11:05 AM Jordan Rose  > wrote:
> [Proposal: 
> https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md
>  
> ]
> 
> Hi, Tony. Glad to see this back again!
> 
> Overall I'm an enthusiastic +1. The restrictions and future work you've 
> listed make sense, and I think this is the right starting place. I just have 
> one thing I'd want to clarify:
> 
>> Any user-provided implementations of == or hashValue will override the 
>> default implementations that would be provided by the compiler.
> 
> Does this include implementations in (possibly constrained) protocol 
> extensions? I assume yes, but that's probably worth calling out explicitly. 
> Still, it could be confusing to some users.
> 
> Yes, manual implementations added in extensions override the 
> compiler-synthesized default:
> 
> Without constraints:
> (swift) struct Foo: Equatable { let x: Int }
> (swift) Foo(x: 5) == Foo(x: 6)
> // r0 : Bool = false
> (swift) Foo(x: 5) == Foo(x: 5)
> // r1 : Bool = true
> (swift) extension Foo { static func ==(lhs: Foo, rhs: Foo) -> Bool { return 
> lhs.x % 2 == rhs.x % 2 } }
> (swift) Foo(x: 5) == Foo(x: 6)
> // r2 : Bool = false
> (swift) Foo(x: 5) == Foo(x: 7)
> // r3 : Bool = true
> 
> With constraints:
> (swift) struct Foo: Equatable { let t: T }
> (swift) extension Foo where T == String { static func ==(lhs: Foo, rhs: 
> Foo) -> Bool { return lhs.t.characters.count == rhs.t.characters.count } }
> (swift) Foo(t: "foo") == Foo(t: "bar")
> // r0 : Bool = true
> (swift) Foo(t: 5) == Foo(t: 7)
> // r1 : Bool = false
> 
> I can update the text to make this explicit.

Ah, that's not quite the example I meant, and your example isn't a correct 
demonstration for the REPL. If you want to test the == that's used in the 
Equatable conformance, you have to call a function that's generic on Equatable.

Anyway, this is the example I meant:

protocol Annoying {}
extension Annoying {
  static func ==(lhs: Self, rhs: Self) -> Bool {
print("annoying")
return true
  }
}
struct Foo: Equatable, Annoying {
  let x: Int
}
print(Foo(x: 5) == Foo(x: 6))

I think the correct behavior here is to call the version from Annoying, but I 
can also see how that would be surprising.

Jordan

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


Re: [swift-evolution] Enums and Source Compatibility

2017-08-10 Thread Jordan Rose via swift-evolution


> On Aug 10, 2017, at 09:15, Vladimir.S via swift-evolution 
>  wrote:
> 
> On 10.08.2017 18:22, Adrian Zubarev via swift-evolution wrote:
>> I think this design does not avoid you writing something like `private enum 
>> Foo { default ... }`, which is redudant as Jordan already pointed out in his 
>> previous post, nor does it have a consistent way of declaration:
> 
> Why compiler can't require 'open' enums to be public only? So, you can write 
> but this will not compile. No?
> 
>> enum Foo {
>>   case abc
>>   case def
>>   default
>> }
>> enum Foo {
>>   case abc
>>   default
>>   case def
>> }
>> enum Foo {
>>   default
>>   case abc
>>   case def
>> }
>> 
> 
> Why compiler can't require 'default' be declared only after last 'case' ?
> 
>> On the other hand I'd be very much in favor of the design David has pitched, 
>> which makes `public` as a soft default for public API's and adds a stronger 
>> constraints with an extra keyword OR a different access modifier. In case of 
>> an access modifier it would be really interesting if someone knows other use 
>> cases where we could reuse `closed` for similar purposes.
>> Long story short:
>> - public is the soft default and all uses of an enum from module A in module 
>> B would require `default` in swithch statements
>> - closed is the stronger implied `public` which makes the enum finite and 
>> does not require `default` if you switch on all cases
> 
> Can't agree. Default-like 'public' for class also allows compiler to make its 
> optimizations as it is known that there can't be a subclass of published 
> class.
> 
> 'public' for enum will in inverse block optimizations, as there is no 
> guarantee that enum will be the same and will not change in future.

Without commenting on the rest of this at the moment, this is incorrect. A 
'public' class may have subclasses in the same module (public or non-public), 
and a 'public' class in a binary framework is also permitted to become 'open' 
in the future.

Jordan


> 
> IMO the developer of external API should explicitly mark public enum as 
> 'closed' or as 'open', to be concrete if changes in enum should/will lead to 
> new major version of framework(so can't be changed in current version) or 
> most likely this enum will be extended in next minor update and then "it 
> might suddenly grow a new case containing a struct with 5000 Strings in 
> it"(Jordan Rose)

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


Re: [swift-evolution] [Review] SE-0185 - Synthesizing Equatable and Hashable conformance

2017-08-10 Thread Jordan Rose via swift-evolution
[Proposal: 
https://github.com/apple/swift-evolution/blob/master/proposals/0185-synthesize-equatable-hashable.md
 
]

Hi, Tony. Glad to see this back again!

Overall I'm an enthusiastic +1. The restrictions and future work you've listed 
make sense, and I think this is the right starting place. I just have one thing 
I'd want to clarify:

> Any user-provided implementations of == or hashValue will override the 
> default implementations that would be provided by the compiler.

Does this include implementations in (possibly constrained) protocol 
extensions? I assume yes, but that's probably worth calling out explicitly. 
Still, it could be confusing to some users.

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


Re: [swift-evolution] Enums and Source Compatibility - defaults

2017-08-10 Thread Jordan Rose via swift-evolution


> On Aug 9, 2017, at 22:46, David Hart  wrote:
> 
> 
>> On 10 Aug 2017, at 02:42, Jordan Rose > > wrote:
>> 
>> :-) As you've all noted, there are some conflicting concerns for the default:
>> 
>> - Source compatibility: the existing behavior for an unannotated enum is 
>> "closed".
>> - Intuition: if you show someone an enum without an explicit annotation, 
>> they'll probably expect they can switch over it. (I'm going to say this is 
>> why Zach calls it a "sensible default".)
>> - Consistency: switches on an enum in the same module can always be 
>> exhaustive, so having it be different across modules is a bit annoying. (But 
>> 'public' already acts like this.)
>> 
>> vs.
>> 
>> - Library evolution: the default should promise less, so that you have the 
>> opportunity to change it.
>> - Flexibility: you can emulate an exhaustive switch with a non-exhaustive 
>> switch using fatalError, but not the other way around.
>> 
>> All of this is why I suggested it be an explicit annotation in either 
>> direction, but Matthew brought up the "keyword soup" problem—if you have to 
>> write (say) "public finite enum" and "public infinite enum", but would never 
>> write "private finite enum" or "private infinite enum", something is 
>> redundant here. Still, I'm uncomfortable with the default case being the one 
>> that constrains library authors, so at least for binary frameworks (those 
>> compiled "with resilience") I would want that to be explicit. That brings us 
>> to one more concern: how different should binary frameworks be from source 
>> frameworks?
> 
> In terms of intuition and consistency, I think we should really try to learn 
> from the simplicity of public/open:
> 
> * When internal, classes are sub-classable by default for convenience, but 
> can be closed with the final keyword
> * When public, classes are closed to sub-classing for safety, but can be 
> opened up with the open keyword (which implies public).
> 
> If we try to mirror this behaviour (the keywords are just suggestions, not 
> important):
> 
> * When internal, enums are exhaustive by default for convenience, but can be 
> opened-up with the partial keyword
> * When public, enums are non-exhaustive by default for safety, but can be 
> made exhaustive with the exhaustive keyword (which implies public).

This is not a correct understanding of the internal/public distinction for 
classes, though. From inside a module, a public-but-not-open class is still 
subclassable, and similarly a public-but-not-"closed" enum will still be 
exhaustively switched. You don't have to worry about your own module changing 
out from under you.

Jordan

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


Re: [swift-evolution] Enums and Source Compatibility

2017-08-09 Thread Jordan Rose via swift-evolution
Hi, Vladimir. I think framing this as a consumer decision is the wrong place to 
start. There are some enums that definitely make sense to be "closed", all the 
time, with no additional annotations, including Foundation.NSComparisonResult 
and, well, Swift.Optional. (Yes, Optional is special, and we could always 
handle it specially if we needed to, but it would be nice if it used the same 
logic as everything else.)

Beyond that, I think something like what you and Charlie describe (your 
'@exhaustive', Charlie's 'switch!') could make sense, as a way to provide a 
deterministic behavior while still getting compiler warnings for not being 
exhaustive. (Probably the only supported behavior here would be trapping, since 
there is no way to test such a scenario with the libraries you currently have.) 
But I'd like to see a real-world example of exhaustively switching over an enum 
in the SDK before designing a feature around this; I strongly suspect it's not 
something we need…

…in the binary framework case. It may still be interesting for source 
frameworks, particularly C enums in those source frameworks.

Jordan


> On Aug 9, 2017, at 09:57, Vladimir.S <sva...@gmail.com> wrote:
> 
> Sorry if I misunderstood the subject, but shouldn't this also be a *consumer* 
> decision, when one wants to keep own switch exhaustive or it is OK to process 
> all possible future cases in 'default'?
> 
> If enum is 'closed' - nothing is changed, as I understand, we have the same 
> rules for switch as currently.
> 
> If 'external' enum, from other framework, was marked as 'closed' but was 
> actually changed in new version of framework/module - our source code will 
> fail to compile (because we need fix our switch cases) and our binary 
> module(built with previous version of framework) will crash.
> 
> With 'open' enums, depending on situations, I as a consumer of external 
> framework, can decide that it is important to me, in my code, check *each* 
> value of external enum in switch. If new enum case added/changed in external 
> framework - my code must fail to compile and notify me that new case should 
> be processed.
> Once we added 'default' case in 'switch' in our code for 'open' enum - we 
> lost compiler's support to keep our 'switch' exhaustive.
> 
> But from other side, in other situation, I want to process all new cases for 
> 'open' enum in 'default' branch, and so allow my source/compiled code to work 
> with new version of framework(with added cases).
> 
> So, it seems like in both situations we need to explicitly tell what is our 
> decided behavior for new cases for 'open' enum in exhaustive switch : 
> crash/fail to compile or process in 'default' block.
> 
> What if in case of exhaustive switch on 'open' enum, compiler warns us saying 
> "this is an exhaustive switch on 'open' enum, but cases can be added later. 
> clarify what is your decided behaviour. by default your code will crash if 
> new cases are added"
> , then we can add 'default' block to process new future cases or somehow mark 
> that switch as explicitly exhaustive... something like this:
> 
> @exhaustive
> switch openEnum {
>  case .yes : ...
>  case .no : ...
> }
> 
> What I'm missing?
> 
> On 09.08.2017 1:27, Jordan Rose via swift-evolution wrote:
>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to an 
>> issue that's been around for a while: the source compatibility of enums. 
>> Today, it's an error to switch over an enum without handling all the cases, 
>> but this breaks down in a number of ways:
>> - A C enum may have "private cases" that aren't defined inside the original 
>> enum declaration, and there's no way to detect these in a switch without 
>> dropping down to the rawValue.
>> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an 
>> imported enum never produces 'nil', because who knows how anyone's using C 
>> enums anyway?
>> - Adding a new case to a /Swift/ enum in a library breaks any client code 
>> that was trying to switch over it.
>> (This list might sound familiar, and that's because it's from a message of 
>> mine on a thread started by Matthew Johnson back in February called "[Pitch] 
>> consistent public access modifiers". Most of the rest of this email is going 
>> to go the same way, because we still need to make progress here.)
>> At the same time, we really like our exhaustive switches, especially over 
>> enums we define ourselves. And there's a performance side to this whole 
>> thing too; if all cases of an enum are known, it can be passed around much 
>> more efficiently than if it might suddenly grow a new case containing a 
>> struct with 5000 Str

Re: [swift-evolution] Enums and Source Compatibility - defaults

2017-08-09 Thread Jordan Rose via swift-evolution
Ah, I forgot to mention: per my response to Charlie, the vast majority of enums 
in ObjC Foundation (80-90%) seem to be "open". I should have put that in the 
"open" side of the list. I'm not convinced that "APIs are designed differently 
in Swift" to the extent where you'd actually expect most enums to be closed; 
rather, I suspect most Swift API designers haven't been thinking too hard about 
exhaustive switches, and certainly not about binary compatibility, which 
becomes relevant at least for Apple once Swift libraries become part of the SDK.

Jordan


> On Aug 9, 2017, at 17:42, Jordan Rose <jordan_r...@apple.com> wrote:
> 
> :-) As you've all noted, there are some conflicting concerns for the default:
> 
> - Source compatibility: the existing behavior for an unannotated enum is 
> "closed".
> - Intuition: if you show someone an enum without an explicit annotation, 
> they'll probably expect they can switch over it. (I'm going to say this is 
> why Zach calls it a "sensible default".)
> - Consistency: switches on an enum in the same module can always be 
> exhaustive, so having it be different across modules is a bit annoying. (But 
> 'public' already acts like this.)
> 
> vs.
> 
> - Library evolution: the default should promise less, so that you have the 
> opportunity to change it.
> - Flexibility: you can emulate an exhaustive switch with a non-exhaustive 
> switch using fatalError, but not the other way around.
> 
> All of this is why I suggested it be an explicit annotation in either 
> direction, but Matthew brought up the "keyword soup" problem—if you have to 
> write (say) "public finite enum" and "public infinite enum", but would never 
> write "private finite enum" or "private infinite enum", something is 
> redundant here. Still, I'm uncomfortable with the default case being the one 
> that constrains library authors, so at least for binary frameworks (those 
> compiled "with resilience") I would want that to be explicit. That brings us 
> to one more concern: how different should binary frameworks be from source 
> frameworks?
> 
> Jordan
> 
> 
> 
>> On Aug 9, 2017, at 08:19, Zach Waldowski via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> I disagree. Closed is indeed the stronger guarantee, but APIs are designed 
>> differently in Swift; closed is a sensible default. We shouldn’t need to 
>> define new keywords and increase the surface area of the language for 
>> something that has verisimilitude with the existing open syntax.
>> 
>> Sincerely,
>>   Zachary Waldowski
>>   z...@waldowski.me <mailto:z...@waldowski.me>
>> 
>> 
>> On Wed, Aug 9, 2017, at 06:23 AM, David Hart via swift-evolution wrote:
>>> 
>>> 
>>> On 9 Aug 2017, at 09:21, Adrian Zubarev via swift-evolution 
>>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>> Hi Jordan, is that only me or haven't you metioned the default should be 
>>>> applied to all new enums? Personally I'd say that 'closed' should be the 
>>>> default and the 'open' enum would require an extra keyword.
>>> 
>>> I think it should definitely be the other way round for public enums 
>>> because closed is the stronger guarantee. Final is the default for classes 
>>> because open is the stronger guarantee. That’s probably why we should not 
>>> use the same keywords.
>>> 
>>>> Now about the keyword itself. Here are two keywords that IMHO nail their 
>>>> behavior down to the point:
>>>> 
>>>> finite enum A {} - so to say a closed enum (default)
>>>> infinite enum B {} - so to say an open enum (requires default case in a 
>>>> switch statement)
>>>> 
>>>> If you think the default should be the other way around, than feel free to 
>>>> switch that. 'finite' also implies that the enum connot ever be extended 
>>>> with more cases (to become infinite), which was also mentioned in your 
>>>> email.
>>>> 
>>>> -- 
>>>> Adrian Zubarev
>>>> Sent with Airmail
>>>> Am 9. August 2017 um 00:27:53, Jordan Rose via swift-evolution 
>>>> (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:
>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to 
>>>>> an issue that's been around for a wh

Re: [swift-evolution] Enums and Source Compatibility - defaults

2017-08-09 Thread Jordan Rose via swift-evolution
:-) As you've all noted, there are some conflicting concerns for the default:

- Source compatibility: the existing behavior for an unannotated enum is 
"closed".
- Intuition: if you show someone an enum without an explicit annotation, 
they'll probably expect they can switch over it. (I'm going to say this is why 
Zach calls it a "sensible default".)
- Consistency: switches on an enum in the same module can always be exhaustive, 
so having it be different across modules is a bit annoying. (But 'public' 
already acts like this.)

vs.

- Library evolution: the default should promise less, so that you have the 
opportunity to change it.
- Flexibility: you can emulate an exhaustive switch with a non-exhaustive 
switch using fatalError, but not the other way around.

All of this is why I suggested it be an explicit annotation in either 
direction, but Matthew brought up the "keyword soup" problem—if you have to 
write (say) "public finite enum" and "public infinite enum", but would never 
write "private finite enum" or "private infinite enum", something is redundant 
here. Still, I'm uncomfortable with the default case being the one that 
constrains library authors, so at least for binary frameworks (those compiled 
"with resilience") I would want that to be explicit. That brings us to one more 
concern: how different should binary frameworks be from source frameworks?

Jordan



> On Aug 9, 2017, at 08:19, Zach Waldowski via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> I disagree. Closed is indeed the stronger guarantee, but APIs are designed 
> differently in Swift; closed is a sensible default. We shouldn’t need to 
> define new keywords and increase the surface area of the language for 
> something that has verisimilitude with the existing open syntax.
> 
> Sincerely,
>   Zachary Waldowski
>   z...@waldowski.me <mailto:z...@waldowski.me>
> 
> 
> On Wed, Aug 9, 2017, at 06:23 AM, David Hart via swift-evolution wrote:
>> 
>> 
>> On 9 Aug 2017, at 09:21, Adrian Zubarev via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>> Hi Jordan, is that only me or haven't you metioned the default should be 
>>> applied to all new enums? Personally I'd say that 'closed' should be the 
>>> default and the 'open' enum would require an extra keyword.
>> 
>> I think it should definitely be the other way round for public enums because 
>> closed is the stronger guarantee. Final is the default for classes because 
>> open is the stronger guarantee. That’s probably why we should not use the 
>> same keywords.
>> 
>>> Now about the keyword itself. Here are two keywords that IMHO nail their 
>>> behavior down to the point:
>>> 
>>> finite enum A {} - so to say a closed enum (default)
>>> infinite enum B {} - so to say an open enum (requires default case in a 
>>> switch statement)
>>> 
>>> If you think the default should be the other way around, than feel free to 
>>> switch that. 'finite' also implies that the enum connot ever be extended 
>>> with more cases (to become infinite), which was also mentioned in your 
>>> email.
>>> 
>>> -- 
>>> Adrian Zubarev
>>> Sent with Airmail
>>> Am 9. August 2017 um 00:27:53, Jordan Rose via swift-evolution 
>>> (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:
>>> 
>>>> 
>>>> 
>>>> 
>>>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to 
>>>> an issue that's been around for a while: the source compatibility of 
>>>> enums. Today, it's an error to switch over an enum without handling all 
>>>> the cases, but this breaks down in a number of ways: 
>>>> 
>>>> - A C enum may have "private cases" that aren't defined inside the 
>>>> original enum declaration, and there's no way to detect these in a switch 
>>>> without dropping down to the rawValue.
>>>> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an 
>>>> imported enum never produces 'nil', because who knows how anyone's using C 
>>>> enums anyway?
>>>> - Adding a new case to a Swift enum in a library breaks any client code 
>>>> that was trying to switch over it.
>>>> 
>>>> (This list might sound familiar, and that's because it's from a message of 
>>>> mine on a thread started by Matthew Johnson back in February called 
>>>> "[Pitch] consistent public access modifiers&quo

Re: [swift-evolution] Enums and Source Compatibility

2017-08-09 Thread Jordan Rose via swift-evolution


> On Aug 9, 2017, at 11:45, Dave DeLong <del...@apple.com> wrote:
> 
> 
>> On Aug 8, 2017, at 4:27 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to an 
>> issue that's been around for a while: the source compatibility of enums. 
>> Today, it's an error to switch over an enum without handling all the cases, 
>> but this breaks down in a number of ways:
>> 
>> - A C enum may have "private cases" that aren't defined inside the original 
>> enum declaration, and there's no way to detect these in a switch without 
>> dropping down to the rawValue.
>> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an 
>> imported enum never produces 'nil', because who knows how anyone's using C 
>> enums anyway?
>> - Adding a new case to a Swift enum in a library breaks any client code that 
>> was trying to switch over it.
>> 
>> (This list might sound familiar, and that's because it's from a message of 
>> mine on a thread started by Matthew Johnson back in February called "[Pitch] 
>> consistent public access modifiers". Most of the rest of this email is going 
>> to go the same way, because we still need to make progress here.)
>> 
>> At the same time, we really like our exhaustive switches, especially over 
>> enums we define ourselves. And there's a performance side to this whole 
>> thing too; if all cases of an enum are known, it can be passed around much 
>> more efficiently than if it might suddenly grow a new case containing a 
>> struct with 5000 Strings in it.
>> 
>> 
>> Behavior
>> 
>> I think there's certain behavior that is probably not terribly controversial:
>> 
>> - When enums are imported from Apple frameworks, they should always require 
>> a default case, except for a few exceptions like NSRectEdge. (It's Apple's 
>> job to handle this and get it right, but if we get it wrong with an imported 
>> enum there's still the workaround of dropping down to the raw value.)
>> - When I define Swift enums in the current framework, there's obviously no 
>> compatibility issues; we should allow exhaustive switches.
>> 
>> Everything else falls somewhere in the middle, both for enums defined in 
>> Objective-C:
>> 
>> - If I define an Objective-C enum in the current framework, should it allow 
>> exhaustive switching, because there are no compatibility issues, or not, 
>> because there could still be private cases defined in a .m file?
>> - If there's an Objective-C enum in another framework (that I built locally 
>> with Xcode, Carthage, CocoaPods, SwiftPM, etc.), should it allow exhaustive 
>> switching, because there are no binary compatibility issues, or not, because 
>> there may be source compatibility issues? We'd really like adding a new enum 
>> case to not be a breaking change even at the source level.
>> - If there's an Objective-C enum coming in through a bridging header, should 
>> it allow exhaustive switching, because I might have defined it myself, or 
>> not, because it might be non-modular content I've used the bridging header 
>> to import?
>> 
>> And in Swift:
>> 
>> - If there's a Swift enum in another framework I built locally, should it 
>> allow exhaustive switching, because there are no binary compatibility 
>> issues, or not, because there may be source compatibility issues? Again, 
>> we'd really like adding a new enum case to not be a breaking change even at 
>> the source level.
>> 
>> Let's now flip this to the other side of the equation. I've been talking 
>> about us disallowing exhaustive switching, i.e. "if the enum might grow new 
>> cases you must have a 'default' in a switch". In previous (in-person) 
>> discussions about this feature, it's been pointed out that the code in an 
>> otherwise-fully-covered switch is, by definition, unreachable, and therefore 
>> untestable. This also isn't a desirable situation to be in, but it's 
>> mitigated somewhat by the fact that there probably aren't many framework 
>> enums you should exhaustively switch over anyway. (Think about Apple's 
>> frameworks again.) I don't have a great answer, though.
>> 
>> For people who like exhaustive switches, we thought about adding a new kind 
>> of 'default'—let's call it 'unknownCase' just to be able to talk about it. 
>> This lets you get warnings when you update to a new SDK, but is even more 
>> likely to be untested 

Re: [swift-evolution] Enums and Source Compatibility

2017-08-09 Thread Jordan Rose via swift-evolution


> On Aug 9, 2017, at 10:15, Tony Allevato via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> On Wed, Aug 9, 2017 at 9:40 AM David Sweeris via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> (Now with more mailing lists in the "to" field!)
> On Aug 8, 2017, at 3:27 PM, Jordan Rose via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> 
>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to an 
>> issue that's been around for a while: the source compatibility of enums. 
>> Today, it's an error to switch over an enum without handling all the cases, 
>> but this breaks down in a number of ways:
>> 
>> - A C enum may have "private cases" that aren't defined inside the original 
>> enum declaration, and there's no way to detect these in a switch without 
>> dropping down to the rawValue.
>> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an 
>> imported enum never produces 'nil', because who knows how anyone's using C 
>> enums anyway?
>> - Adding a new case to a Swift enum in a library breaks any client code that 
>> was trying to switch over it.
>> 
>> (This list might sound familiar, and that's because it's from a message of 
>> mine on a thread started by Matthew Johnson back in February called "[Pitch] 
>> consistent public access modifiers". Most of the rest of this email is going 
>> to go the same way, because we still need to make progress here.)
>> 
>> At the same time, we really like our exhaustive switches, especially over 
>> enums we define ourselves. And there's a performance side to this whole 
>> thing too; if all cases of an enum are known, it can be passed around much 
>> more efficiently than if it might suddenly grow a new case containing a 
>> struct with 5000 Strings in it.
>> 
>> 
>> Behavior
>> 
>> I think there's certain behavior that is probably not terribly controversial:
>> 
>> - When enums are imported from Apple frameworks, they should always require 
>> a default case, except for a few exceptions like NSRectEdge. (It's Apple's 
>> job to handle this and get it right, but if we get it wrong with an imported 
>> enum there's still the workaround of dropping down to the raw value.)
>> - When I define Swift enums in the current framework, there's obviously no 
>> compatibility issues; we should allow exhaustive switches.
>> 
>> Everything else falls somewhere in the middle, both for enums defined in 
>> Objective-C:
>> 
>> - If I define an Objective-C enum in the current framework, should it allow 
>> exhaustive switching, because there are no compatibility issues, or not, 
>> because there could still be private cases defined in a .m file?
>> - If there's an Objective-C enum in another framework (that I built locally 
>> with Xcode, Carthage, CocoaPods, SwiftPM, etc.), should it allow exhaustive 
>> switching, because there are no binary compatibility issues, or not, because 
>> there may be source compatibility issues? We'd really like adding a new enum 
>> case to not be a breaking change even at the source level.
>> - If there's an Objective-C enum coming in through a bridging header, should 
>> it allow exhaustive switching, because I might have defined it myself, or 
>> not, because it might be non-modular content I've used the bridging header 
>> to import?
>> 
>> And in Swift:
>> 
>> - If there's a Swift enum in another framework I built locally, should it 
>> allow exhaustive switching, because there are no binary compatibility 
>> issues, or not, because there may be source compatibility issues? Again, 
>> we'd really like adding a new enum case to not be a breaking change even at 
>> the source level.
>> 
>> Let's now flip this to the other side of the equation. I've been talking 
>> about us disallowing exhaustive switching, i.e. "if the enum might grow new 
>> cases you must have a 'default' in a switch". In previous (in-person) 
>> discussions about this feature, it's been pointed out that the code in an 
>> otherwise-fully-covered switch is, by definition, unreachable, and therefore 
>> untestable. This also isn't a desirable situation to be in, but it's 
>> mitigated somewhat by the fact that there probably aren't many framework 
>> enums you should exhaustively switch over anyway. (Think about Apple's 
>> frameworks again.) I don't have a great answer, though.
>> 
>> For people who like exhaustive switc

Re: [swift-evolution] Enums and Source Compatibility

2017-08-09 Thread Jordan Rose via swift-evolution
There really isn't a good way to do "extensible outside the library" enums, 
because at that point you've lost the guarantee of distinct cases—it's possible 
for two modules to add the same case with the same raw value. So I don't think 
there are really three states for public enums, only two. I do sympathize with 
the keyword soup problem, though.

(Also, let's please leave protocols out of the enum discussion. Nearly all the 
reasons to give protocols a public/open distinction come from classes, not 
enums, and I wouldn't want to tie the success or failure of one proposal to the 
other.)

The issue of a default is an important one; I'll respond to it on Adrian's 
branch of the thread.

Jordan


> On Aug 9, 2017, at 09:07, Matthew Johnson <matt...@anandabits.com> wrote:
> 
> Hi Jordan,
> 
> Thanks for bringing this topic up again!  I’m glad to see it will receive 
> attention in Swift 5.  I agree with the semantics of your proposed direction. 
>  
> 
> In terms of syntax, I continue to believe that requiring users to specify a 
> keyword indicating open or closed *in addition* to public would be 
> unfortunate.  Open / closed is only relevant for public enums and therefore 
> implies public.  We’ve done a really good job of avoiding keyword soup in 
> Swift and the way that open classes are implicitly public is a good precedent 
> that we should follow.
> 
> I also continue to believe that aligning protocols, enums and classes to use 
> consistent terminology for similar concepts has many advantages.  The 
> semantics would be:
> 
> * open: Extensible outside the library
> * public: Extensible in future versions of the library (or privately by the 
> library)
> * closed: Fixed set of publicly visible cases / subclasses / conformances 
> defined by the library and guaranteed not to change without breaking ABI and 
> source compatibility.
> 
> This approach makes public a “soft default” that preserves maximum 
> flexibility for the library author while allowing them to make a stronger 
> guarantee of user-extensibility or completeness by changing (rather than 
> adding) a keyword.  It also highlights the symmetry of the two very different 
> user-guarantees a library may choose to support.
> 
> As noted in my previous thread, this approach would require a migration for 
> protocols as well as enums as the current behavior of public protocols is to 
> allow conformances outside the library.
> 
> There are certainly reasonable arguments to be made for other approaches, 
> particularly if there is no appetite for changing the semantics of public 
> protocols (which seems likely).  Nevertheless, I think we should keep the 
> merits of consistency in mind and understand the benefits of alternatives 
> relative to the more consistent approach as we evaluate them.
> 
> In terms of alternatives, what is your opinion on using public as a “soft 
> default” and assigning it one of the two enum semantics you discuss?  Do you 
> think this makes sense or would you prefer distinct keywords for these two 
> semantics?  I don’t have any really great new ideas, but I’ll throw out 
> “complete” and “incomplete” as a possibility.
> 
> My two cents for now…
> 
> Matthew
> 
> 
>> On Aug 8, 2017, at 5:27 PM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to an 
>> issue that's been around for a while: the source compatibility of enums. 
>> Today, it's an error to switch over an enum without handling all the cases, 
>> but this breaks down in a number of ways:
>> 
>> - A C enum may have "private cases" that aren't defined inside the original 
>> enum declaration, and there's no way to detect these in a switch without 
>> dropping down to the rawValue.
>> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an 
>> imported enum never produces 'nil', because who knows how anyone's using C 
>> enums anyway?
>> - Adding a new case to a Swift enum in a library breaks any client code that 
>> was trying to switch over it.
>> 
>> (This list might sound familiar, and that's because it's from a message of 
>> mine on a thread started by Matthew Johnson back in February called "[Pitch] 
>> consistent public access modifiers". Most of the rest of this email is going 
>> to go the same way, because we still need to make progress here.)
>> 
>> At the same time, we really like our exhaustive switches, especially over 
>> enums we define ourselves. And there's a performance side to this whole 
>> thing too; if all cases of an enum are known

Re: [swift-evolution] Enums and Source Compatibility

2017-08-09 Thread Jordan Rose via swift-evolution
Hi, Charlie. This is fair—if you're switching over an open enum at all, 
presumably you have a reason for doing so and therefore might want to handle 
all known cases every time you update your SDK. However, I think in practice 
that's going to be rare—do you have examples of exhaustive switches on SDK 
enums that exist in your own app?

(There's an additional piece about how to handle cases with different 
availability—there's nowhere obvious to write the #available.)

I suspect marking SDK enums "closed" will be much easier than nullability, 
simply because there are so few of them. Here's some data to that effect: out 
of all  60 or so NS_ENUMs in Foundation, only 6 of them are ones I would 
definitely mark "closed":

- NSComparisonResult
- NSKeyValueChange / NSKeyValueSetMutationKind
- NSRectEdge
- NSURLRelationship
- maybe NSCalculationError

There are a few more, like NSURLHandleStatus, where I could see someone wanting 
to exhaustively switch as well, but the main point is that there is a clear 
default for public enums, at least in Objective-C, and that even in a large 
framework it's not too hard to look at all of them.

(Note that NSComparisonResult is technically not part of Foundation; it lives 
in the ObjectiveC module as /usr/include/objc/NSObject.h.)

Jordan


> On Aug 8, 2017, at 21:53, Charlie Monroe <char...@charliemonroe.net> wrote:
> 
> While I agree with the entire idea and would actually use behavior like this 
> in a few instances, I feel that in most cases, you would simply put 
> 
> default:
>   fatalError()
> 
> The huge downside of this is that you no longer get warned by the compiler 
> that you are missing a case that was added - a common thing I personally do 
> (and I have a feeling I'm not alone) - add an enum case, build the app, see 
> what broke and fix it - as you get warnings/errors about the switch not being 
> exhaustive. You find this out during runtime (if you're lucky), otherwise 
> your end user.
> 
> As you've noted all enums from ObjC would need to be marked with an 
> annotation marking if they are closed - which given the way nullability is 
> still missing in many frameworks out there, I think would take years.
> 
> I'd personally expand this proposal by introducing switch! (with the 
> exclamation mark) which would allow to treat open enums as closed. Example:
> 
> // Imported from ObjC
> open enum NSAlert.Style { ... }
> 
> switch! alert.style {
> case .warning:
>   // ...
> case .informational:
>   // ...
> case .critical:
>   // ...
> }
> 
> The force-switch would implicitely create the default label crashing, logging 
> the rawValue of the enum.
> 
> Thoughts?
> 
>> On Aug 9, 2017, at 12:28 AM, Jordan Rose via swift-evolution 
>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>> 
>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to an 
>> issue that's been around for a while: the source compatibility of enums. 
>> Today, it's an error to switch over an enum without handling all the cases, 
>> but this breaks down in a number of ways:
>> 
>> - A C enum may have "private cases" that aren't defined inside the original 
>> enum declaration, and there's no way to detect these in a switch without 
>> dropping down to the rawValue.
>> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an 
>> imported enum never produces 'nil', because who knows how anyone's using C 
>> enums anyway?
>> - Adding a new case to a Swift enum in a library breaks any client code that 
>> was trying to switch over it.
>> 
>> (This list might sound familiar, and that's because it's from a message of 
>> mine on a thread started by Matthew Johnson back in February called "[Pitch] 
>> consistent public access modifiers". Most of the rest of this email is going 
>> to go the same way, because we still need to make progress here.)
>> 
>> At the same time, we really like our exhaustive switches, especially over 
>> enums we define ourselves. And there's a performance side to this whole 
>> thing too; if all cases of an enum are known, it can be passed around much 
>> more efficiently than if it might suddenly grow a new case containing a 
>> struct with 5000 Strings in it.
>> 
>> 
>> Behavior
>> 
>> I think there's certain behavior that is probably not terribly controversial:
>> 
>> - When enums are imported from Apple frameworks, they should always require 
>> a default case, except for a few exceptions like NSRectEdge. (It's Apple's 
>> job to handle this and get it right, but if we get it wrong with an imported 
&g

Re: [swift-evolution] overridable members in extensions (was: Why you can't make someone else's class Decodable)

2017-08-07 Thread Jordan Rose via swift-evolution

> On Aug 2, 2017, at 23:21, Goffredo Marocchi <pana...@gmail.com> wrote:
> 
> 
> Sent from my iPhone
> 
> On 3 Aug 2017, at 01:09, Jordan Rose via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> 
>> 
>> 'required' initializers are like methods: they may require dynamic dispatch. 
>> That means that they get an entry in the class's dynamic dispatch table, 
>> commonly known as its vtable. Unlike Objective-C method tables, vtables 
>> aren't set up to have entries arbitrarily added at run time.
>> 
>> (Aside: This is one of the reasons why non-@objc methods in Swift extensions 
>> can't be overridden; if we ever lift that restriction, it'll be by using a 
>> separate table and a form of dispatch similar to objc_msgSend. I sent a 
>> proposal to swift-evolution about this last year but there wasn't much 
>> interest.)
> 
> If I missed replying to that originally I also missed the chance to say that 
> it would be a lovely idea and dynamic dispatch in some cases is just what the 
> doctor ordered (runtime editable method tables).
> This is especially especially important with extensions for classes and 
> default methods (and the current rules for overriding methods in the 
> implementing class), please resubmit the proposal :).

Thanks for the vote of confidence. :-) Here’s the old proposal for now, likely 
to be revised soon. 
https://github.com/jrose-apple/swift-evolution/blob/overridable-members-in-extensions/proposals/-overridable-members-in-extensions.md
 
<https://github.com/jrose-apple/swift-evolution/blob/overridable-members-in-extensions/proposals/-overridable-members-in-extensions.md>

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


[swift-evolution] Why you can't make someone else's class Decodable: a long-winded explanation of 'required' initializers

2017-08-02 Thread Jordan Rose via swift-evolution
David Hart recently asked on Twitter 
 if there was a good 
way to add Decodable support to somebody else's class. The short answer is "no, 
because you don't control all the subclasses", but David already understood 
that and wanted to know if there was anything working to mitigate the problem. 
So I decided to write up a long email about it instead. (Well, actually I 
decided to write a short email and then failed at doing so.)

The Problem

You can add Decodable to someone else's struct today with no problems:

extension Point: Decodable {
  enum CodingKeys: String, CodingKey {
case x
case y
  }
  public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let x = try container.decode(Double.self, forKey: .x)
let y = try container.decode(Double.self, forKey: .y)
self.init(x: x, y: y)
  }
}

But if Point is a (non-final) class, then this gives you a pile of errors:

- init(from:) needs to be 'required' to satisfy a protocol requirement. 
'required' means the initializer can be invoked dynamically on subclasses. Why 
is this important? Because someone might write code like this:

func decodeMe() -> Result {
  let decoder = getDecoderFromSomewhere()
  return Result(from: decoder)
}
let specialPoint: VerySpecialSubclassOfPoint = decodeMe()

…and the compiler can't stop them, because VerySpecialSubclassOfPoint is a 
Point, and Point is Decodable, and therefore VerySpecialSubclassOfPoint is 
Decodable. A bit more on this later, but for now let's say that's a sensible 
requirement.

- init(from:) also has to be a 'convenience' initializer. That one makes sense 
too—if you're outside the module, you can't necessarily see private properties, 
and so of course you'll have to call another initializer that can.

But once it's marked 'convenience' and 'required' we get "'required' 
initializer must be declared directly in class 'Point' (not in an extension)", 
and that defeats the whole purpose. Why this restriction?


The Semantic Reason

The initializer is 'required', right? So all subclasses need to have access to 
it. But the implementation we provided here might not make sense for all 
subclasses—what if VerySpecialSubclassOfPoint doesn't have an 'init(x:y:)' 
initializer? Normally, the compiler checks for this situation and makes the 
subclass reimplement the 'required' initializer…but that only works if the 
'required' initializers are all known up front. So it can't allow this new 
'required' initializer to go by, because someone might try to call it 
dynamically on a subclass. Here's a dynamic version of the code from above:

func decodeDynamic(_ pointType: Point.Type) -> Point {
  let decoder = getDecoderFromSomewhere()
  return pointType.init(from: decoder)
}
let specialPoint = decodeDynamic(VerySpecialSubclassOfPoint.self)


The Implementation Reason

'required' initializers are like methods: they may require dynamic dispatch. 
That means that they get an entry in the class's dynamic dispatch table, 
commonly known as its vtable. Unlike Objective-C method tables, vtables aren't 
set up to have entries arbitrarily added at run time.

(Aside: This is one of the reasons why non-@objc methods in Swift extensions 
can't be overridden; if we ever lift that restriction, it'll be by using a 
separate table and a form of dispatch similar to objc_msgSend. I sent a 
proposal to swift-evolution about this last year but there wasn't much 
interest.)


The Workaround

Today's answer isn't wonderful, but it does work: write a wrapper struct that 
conforms to Decodable instead:

struct DecodedPoint: Decodable {
  var value: Point
  enum CodingKeys: String, CodingKey {
case x
case y
  }
  public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let x = try container.decode(Double.self, forKey: .x)
let y = try container.decode(Double.self, forKey: .y)
self.value = Point(x: x, y: y)
  }
}

This doesn't have any of the problems with inheritance, because it only handles 
the base class, Point. But it makes everywhere else a little less 
convenient—instead of directly encoding or decoding Point, you have to use the 
wrapper, and that means no implicitly-generated Codable implementations either.

I'm not going to spend more time talking about this, but it is the officially 
recommended answer at the moment. You can also just have all your own types 
that contain points manually decode the 'x' and 'y' values and then construct a 
Point from that.


Future Direction: 'required' + 'final'

One language feature we could add to make this work is a 'required' initializer 
that is also 'final'. Because it's 'final', it wouldn't have to go into the 
dynamic dispatch table. But because it's 'final', we have to make sure its 
implementation works on all subclasses. For that to work, it would only be 
allowed to call other 'required' 

Re: [swift-evolution] [Review] SE-0182 - String Newline Escaping

2017-07-14 Thread Jordan Rose via swift-evolution


> On Jul 14, 2017, at 10:00, Alex Blewitt  wrote:
> 
>> On 13 Jul 2017, at 23:14, David Hart via swift-evolution 
>> > wrote:
>> 
>>> 
>>> On 14 Jul 2017, at 00:21, Jordan Rose >> > wrote:
>>> 
>>> [Proposal: 
>>> https://github.com/apple/swift-evolution/blob/master/proposals/0182-newline-escape-in-strings.md
>>>  
>>> ]
>>> 
>>> This is a tiny, tiny point amidst the broader discussions others are 
>>> having, but
>>> 
• All whitespace characters between \ and the newline are disregarded.
>>> 
>>> Why? Why bother allowing whitespace characters between \ and the newline?
>> 
>> The reasoning is to be consistent with trailing whitespace in the rest of 
>> the code: to leave that to a linter instead. Or to see it differently, even 
>> with whitespace between \ and the newline, the programmer’s intent is still 
>> clear. Why generate an error?
> 
> For the same reason that code allows (e.g.) a comment at the end of the line; 
> you wouldn't expect (newline continuation) (comment) to mean the same thing 
> as if generic whitespace were added at the end. The convention in other 
> languages is that \ immediately precedes the line feed to indicate a 
> continuation, not that an orphan \ is valid on its own.
> 
> The reason that \(newline) is valid while \(otherchar)(newline) isn't is 
> because \ immediately precedes another character that it is escaping, and 
> it's possible that \(space) would have a meaning in the future, whereas 
> \(newline) won't.

I agree with Alex on this, although I would be happy to make it a warning 
rather than an error so that it doesn't block compilation.

The point about comments is also significant: we previously said that comments 
should generally be treated like whitespace (SE-0037 
).
 This is a little different because it's still inside the string literal, but 
it's probably worth explicitly stating "we're still inside a string literal; 
you can't just put comments after the backslash".

Jordan

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


  1   2   3   4   >