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

2017-08-13 Thread Tino Heth via swift-evolution
> 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 agree a lot that enums are not nearly as useful as they seem at first sight, 
and I know I'm not the only one who started with an enum-based solution and 
realised later that it doesn't work out as expected.
But I didn't encounter that with enums in Foundation — obviously, because I'm 
not writing Foundation code ;-), so could you give some concrete examples where 
the different alternatives would be a better fit?
My experience is that enums loose their appeal quickly when you want to attach 
behaviour or properties to the different cases… but I think closed protocols 
would also be just a workaround, inferior compared with case classes and ad-hoc 
union types. 

___
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-12 Thread Rod Brown via swift-evolution
Yes, I see your point, and that’s something I didn’t consider. If we allowed 
extending enums, this would then clearly indicate that “final” means something 
in the present as well - you cannot add cases.

This opens up the can of worms of extending enums, but I think it’s a fair 
point if we ever want to consider it that then “final" really would make the 
most sense.


> On 12 Aug 2017, at 8:28 am, Jordan Rose  wrote:
> 
> "final"'s on my list of possible names! But it's not quite the same as 
> 'final' for classes: "closed" only talks about what might change in the 
> future, while 'final'-on-a-class also states something about the class in the 
> present: it has no subclasses now as well as not gaining any new ones in the 
> future. Still, it could be closed enough that it ends up being the best bet.
> 
> Jordan
> 
> 
>> On Aug 10, 2017, at 22:23, Rod Brown > > wrote:
>> 
>> Hi all,
>> 
>> I thought I’d chime in with my own 2c…
>> 
>> I’d probably prefer something more like “final” and vs non-final. It’s the 
>> concept we’re dancing around - can you add something to extend it?
>> 
>> In which case, final would allow exhaustive use, and non-final would require 
>> handling the default case. Currently all Swift API would convert as “final”, 
>> and all imported Obj-C API (with perhaps exceptions) would import as is and 
>> require a default handling case. This negates the open vs public issue. But 
>> it does also mean that it would become a manual issue inside the module to 
>> mark the API as final to allow exhaustive switches, unless we say 
>> “exhaustive switches allowed on internal/fileprivate/private types”.
>> 
>> Unsure how this plays into the web of things, though…
>> 
>> Thanks,
>> 
>> Rod
>> 
>> 
>> 
>>> On 11 Aug 2017, at 9:41 am, Jordan Rose via swift-evolution 
>>> > wrote:
>>> 
>>> 
>>> 
 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 

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 - defaults

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


> On Aug 9, 2017, at 20:00, Zach Waldowski via swift-evolution 
>  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] Enums and Source Compatibility - defaults

2017-08-10 Thread David Hart via swift-evolution


> 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? And what do you think of the idea of having the 
« exhaustiveness » modifier imply public, like open does?

> 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 - defaults

2017-08-09 Thread David Hart via swift-evolution

> 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).

David.

> Jordan
> 
> 
> 
>> On Aug 9, 2017, at 08:19, Zach Waldowski via swift-evolution 
>> > 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 
>> 
>> 
>> 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 
>>> > 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 ) 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 

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

2017-08-09 Thread Zach Waldowski via swift-evolution
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.
Sincerely,
  Zachary Waldowski
  z...@waldowski.me


___
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-09 Thread Zach Waldowski via swift-evolution
On Wed, Aug 9, 2017, at 08:42 PM, Jordan Rose via swift-evolution wrote:> - 
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.)
>From someone frequently working on teams or unfamiliar codebases, it’s
really heard to know whether “default: fatalError()” is a smell or not
without memorizing the  surface area of every API your app works with.
> … That brings us to one more concern: how different should binary
> frameworks be from source frameworks?
As few as possible. Realistically, I understand there must be  some, but
I feel like they need to be there for awfully good reasons. The
differences between app targets, framework/library targets, and
playgrounds feel arbitrary unless you understand parts of the language
very fully.
Some debugging techniques (or even just saving-time-rebuilding
techniques) involve using a binary framework and swapping it for another
binary with extra instrumentation or a source version for full
instrumentation, or vice-versa as you ossify a PoC feature into being
production-ready. It would be awful to break source compatibility as
this happens because an API you’re consuming changes behavior according
to the compiler. (I can hear my graybeard coworkers already - “It’s the
same code!”)
Sincerely,
  Zachary Waldowski
  z...@waldowski.me


___
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-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  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 
>> > 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 
>> 
>> 
>> 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 
>>> > 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 ) 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 

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 
>  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 
> 
> 
> 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 
>> > 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 ) 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". 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,