Enums and Source Compatibility

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 <https://www.remind.com/> | BLOG <http://blog.remind.com/>
> FOLLOW
US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>

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 <rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>

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> 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 <rex@remind101.com <mailto:rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>

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

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> 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 <rex@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 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

Hi Jordan,

I've been keeping up with most of the discussion since I last emailed about
my concern about making nonexhaustive the default mode for enums. So far I
am still strongly in the camp of exhaustive by default.

Most of my understanding comes from the perspective of source
compatibility; since I'm primarily an iOS app developer and contributor to
open source through CocoaPods, I don't have enough experience with binary
compatibility issues to understand the breadth of the effects of such a
change on binary compatibility - so I will argue from the perspective of
source compatibility. Additionally, all the following applies exclusively
to enums from Swift and not Obj-C/C. Largely my concerns stem from the "default
effect <https://en.m.wikipedia.org/wiki/Default_effect_(psychology)>"-
whatever default is chosen is going to unconsciously bias developers to use
that default.

To start, I don't follow how nonexhaustive by default leads to more secure
code or promises less. In a world where nonexhaustive is default,
hard-to-track bugs will be introduced frequently into many Swift
developer's code.

   - Evolving contracts - The change you suggest leaves it up to the user
   to remember to add new cases into their code if an enum ever does change in
   a framework, and their code relies on exhaustive pattern matching.
   - Unhandled case bugs - This will lead to inconsistencies and
   hard-to-track bugs, since it would require the user to then track down an
   unhandled case that they may not even be aware exists (forcing googling,
   searching through documentation, etc.).

This is something that I certainly have experienced in the past working
across teams programming in Obj-C, and Swift has so far completely
eliminated this class of bugs! In a world where exhaustive remains default,
nothing changes, fewer bugs.

Next off is developer usability. As someone who has contributed to several
frameworks in Swift, one thing I know is that you can always count on
people complaining about the usability of their library - it's something
you learn to expect. If it turns out that people are very frustrated with
an enum constantly changing and breaking compatibility, they will voice
that concern with a thousand :+1: on github, debate will happen, and the
appropriate course correction will be made. In this case, no real damage
done.

That said, if enums are nonexhaustive by default, frameworks will have more
nonexhaustive enums (as it becomes convention). The class of bugs
previously discussed will emerge, causing real damage to users and
developers. Complaints will arise, but with more hostility. In this case,
we end up back where we started, since framework developers will then mark
exhaustive for all their enums. The only difference is that it'll be
something that must be remembered to be done.

I can understand how someone developing an Apple framework may want
nonexhaustive by default, since enums from some Apple libraries seem to be
much larger and change relatively often. Yet, from my experience, this
doesn't represent what you find in open source libraries that seem to land
on something consistent and then stick with it. And that consistency pairs
very well with exhaustiveness.

Given this, it's clear that adding a case to an enum as a source breaking
change should be the expected behavior. It's safer for those using the
frameworks, and back propagation from users will correct it if it becomes
an annoyance. Nonexhaustive as a keyword is a nice additional tool to make
this correction simpler (as well as protect C enums by default), but should
not be the standard.

Best,
Rex

···

On Fri, Sep 15, 2017 at 5:06 PM, Jordan Rose <jordan_rose@apple.com> 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 <rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> |
FOLLOW US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>

--

Rex Fenley | IOS DEVELOPER

Remind.com <https://www.remind.com/> | BLOG <http://blog.remind.com/>
> FOLLOW
US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>

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 <jon@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 <rex@remind101.com <mailto:rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/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 <panajev@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 <rex@remind101.com <mailto:rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>

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

Hi Jordan,

I've been keeping up with most of the discussion since I last emailed about my concern about making nonexhaustive the default mode for enums. So far I am still strongly in the camp of exhaustive by default.

Most of my understanding comes from the perspective of source compatibility; since I'm primarily an iOS app developer and contributor to open source through CocoaPods, I don't have enough experience with binary compatibility issues to understand the breadth of the effects of such a change on binary compatibility - so I will argue from the perspective of source compatibility. Additionally, all the following applies exclusively to enums from Swift and not Obj-C/C. Largely my concerns stem from the "default effect <https://en.m.wikipedia.org/wiki/Default_effect_(psychology)>"- whatever default is chosen is going to unconsciously bias developers to use that default.

To start, I don't follow how nonexhaustive by default leads to more secure code or promises less. In a world where nonexhaustive is default, hard-to-track bugs will be introduced frequently into many Swift developer's code.
Evolving contracts - The change you suggest leaves it up to the user to remember to add new cases into their code if an enum ever does change in a framework, and their code relies on exhaustive pattern matching.

I think this is the right thing to do. Library vendors can still communicate changes with clients through deprecation notices. Ultimately it is up to the vendor to maintain compatibility for people switching against those older cases, and up to the clients to adopt any new behaviour that gets implemented.

Unhandled case bugs - This will lead to inconsistencies and hard-to-track bugs, since it would require the user to then track down an unhandled case that they may not even be aware exists (forcing googling, searching through documentation, etc.).
This is something that I certainly have experienced in the past working across teams programming in Obj-C, and Swift has so far completely eliminated this class of bugs! In a world where exhaustive remains default, nothing changes, fewer bugs.

I’m not sure that those issues are so difficult to track down. You’ll know that switch statements which fall-through to a default case are suspicious - but they already are today, too!. It’s not really any different to having allowing singular “if”s without a corresponding “else”. I could also see the debugging value in a “future” case label, but it’s not necessary IMO.

Although, I can kind of see your point:

If you’re talking about a private framework (e.g. the multi-module App scenario), then things are a little different for you because typically, you’ll ship new versions of the framework and App together. The framework can be as fragile as you like in that case - you can break all the ABI stability rules, because there will no apps importing the new version of the framework who were compiled against older versions of it. In many ways, those frameworks are really like static libraries (with extras, like resources).

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.

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.

- Karl

···

On 21. Sep 2017, at 00:51, Rex Fenley via swift-evolution <swift-evolution@swift.org> wrote:

Next off is developer usability. As someone who has contributed to several frameworks in Swift, one thing I know is that you can always count on people complaining about the usability of their library - it's something you learn to expect. If it turns out that people are very frustrated with an enum constantly changing and breaking compatibility, they will voice that concern with a thousand :+1: on github, debate will happen, and the appropriate course correction will be made. In this case, no real damage done.

That said, if enums are nonexhaustive by default, frameworks will have more nonexhaustive enums (as it becomes convention). The class of bugs previously discussed will emerge, causing real damage to users and developers. Complaints will arise, but with more hostility. In this case, we end up back where we started, since framework developers will then mark exhaustive for all their enums. The only difference is that it'll be something that must be remembered to be done.

I can understand how someone developing an Apple framework may want nonexhaustive by default, since enums from some Apple libraries seem to be much larger and change relatively often. Yet, from my experience, this doesn't represent what you find in open source libraries that seem to land on something consistent and then stick with it. And that consistency pairs very well with exhaustiveness.

Given this, it's clear that adding a case to an enum as a source breaking change should be the expected behavior. It's safer for those using the frameworks, and back propagation from users will correct it if it becomes an annoyance. Nonexhaustive as a keyword is a nice additional tool to make this correction simpler (as well as protect C enums by default), but should not be the standard.

Best,
Rex

On Fri, Sep 15, 2017 at 5:06 PM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> 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 <rex@remind101.com <mailto:rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>

--
Rex Fenley | IOS DEVELOPER

Remind.com <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Since I was just looking at the Default section, it wasn’t clear that the default was just for the external case. But mostly I just wasn’t paying close attention. Also, it’s not entirely clear whether nonexhaustive enums can be used within the same module, but again, I may just be missing something.

Jon

···

On Sep 18, 2017, at 1:09 PM, Jordan Rose <jordan_rose@apple.com> wrote:

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 <jon@jonshier.com <mailto:jon@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 <rex@remind101.com <mailto:rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>

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

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.

Dave

···

On Sep 18, 2017, at 11:48 AM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

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 <panajev@gmail.com <mailto:panajev@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 <rex@remind101.com <mailto:rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> | FOLLOW US <https://twitter.com/remindhq> | LIKE US <https://www.facebook.com/remindhq>

_______________________________________________
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

Of course, you can’t actually use static libraries because on iOS, for example, you might have XPC services or other mini-applications which also want to use your model libraries. Static libraries would bloat your binary size.

Is there some kind of middle-ground, perhaps? Like a fragile dynamic library, which makes no attempt to be resilient?

- Karl

···

On 22. Sep 2017, at 17:21, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

On 21. Sep 2017, at 00:51, Rex Fenley via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Jordan,

I've been keeping up with most of the discussion since I last emailed about my concern about making nonexhaustive the default mode for enums. So far I am still strongly in the camp of exhaustive by default.

Most of my understanding comes from the perspective of source compatibility; since I'm primarily an iOS app developer and contributor to open source through CocoaPods, I don't have enough experience with binary compatibility issues to understand the breadth of the effects of such a change on binary compatibility - so I will argue from the perspective of source compatibility. Additionally, all the following applies exclusively to enums from Swift and not Obj-C/C. Largely my concerns stem from the "default effect <https://en.m.wikipedia.org/wiki/Default_effect_(psychology)>"- whatever default is chosen is going to unconsciously bias developers to use that default.

To start, I don't follow how nonexhaustive by default leads to more secure code or promises less. In a world where nonexhaustive is default, hard-to-track bugs will be introduced frequently into many Swift developer's code.
Evolving contracts - The change you suggest leaves it up to the user to remember to add new cases into their code if an enum ever does change in a framework, and their code relies on exhaustive pattern matching.

I think this is the right thing to do. Library vendors can still communicate changes with clients through deprecation notices. Ultimately it is up to the vendor to maintain compatibility for people switching against those older cases, and up to the clients to adopt any new behaviour that gets implemented.

Unhandled case bugs - This will lead to inconsistencies and hard-to-track bugs, since it would require the user to then track down an unhandled case that they may not even be aware exists (forcing googling, searching through documentation, etc.).
This is something that I certainly have experienced in the past working across teams programming in Obj-C, and Swift has so far completely eliminated this class of bugs! In a world where exhaustive remains default, nothing changes, fewer bugs.

I’m not sure that those issues are so difficult to track down. You’ll know that switch statements which fall-through to a default case are suspicious - but they already are today, too!. It’s not really any different to having allowing singular “if”s without a corresponding “else”. I could also see the debugging value in a “future” case label, but it’s not necessary IMO.

Although, I can kind of see your point:

If you’re talking about a private framework (e.g. the multi-module App scenario), then things are a little different for you because typically, you’ll ship new versions of the framework and App together. The framework can be as fragile as you like in that case - you can break all the ABI stability rules, because there will no apps importing the new version of the framework who were compiled against older versions of it. In many ways, those frameworks are really like static libraries (with extras, like resources).

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?

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?

Thanks for your input

···

On Fri, Sep 22, 2017 at 8:20 AM, Karl Wagner <razielim@gmail.com> wrote:

On 21. Sep 2017, at 00:51, Rex Fenley via swift-evolution < > swift-evolution@swift.org> wrote:

Hi Jordan,

I've been keeping up with most of the discussion since I last emailed
about my concern about making nonexhaustive the default mode for enums. So
far I am still strongly in the camp of exhaustive by default.

Most of my understanding comes from the perspective of source
compatibility; since I'm primarily an iOS app developer and contributor to
open source through CocoaPods, I don't have enough experience with binary
compatibility issues to understand the breadth of the effects of such a
change on binary compatibility - so I will argue from the perspective of
source compatibility. Additionally, all the following applies exclusively
to enums from Swift and not Obj-C/C. Largely my concerns stem from the "default
effect <https://en.m.wikipedia.org/wiki/Default_effect_(psychology)>"-
whatever default is chosen is going to unconsciously bias developers to use
that default.

To start, I don't follow how nonexhaustive by default leads to more secure
code or promises less. In a world where nonexhaustive is default,
hard-to-track bugs will be introduced frequently into many Swift
developer's code.

   - Evolving contracts - The change you suggest leaves it up to the user
   to remember to add new cases into their code if an enum ever does change in
   a framework, and their code relies on exhaustive pattern matching.

I think this is the right thing to do. Library vendors can still
communicate changes with clients through deprecation notices. Ultimately it
is up to the vendor to maintain compatibility for people switching against
those older cases, and up to the clients to adopt any new behaviour that
gets implemented.

   - Unhandled case bugs - This will lead to inconsistencies and
   hard-to-track bugs, since it would require the user to then track down an
   unhandled case that they may not even be aware exists (forcing googling,
   searching through documentation, etc.).

This is something that I certainly have experienced in the past working
across teams programming in Obj-C, and Swift has so far completely
eliminated this class of bugs! In a world where exhaustive remains default,
nothing changes, fewer bugs.

I’m not sure that those issues are so difficult to track down. You’ll know
that switch statements which fall-through to a default case are suspicious
- but they already are today, too!. It’s not really any different to having
allowing singular “if”s without a corresponding “else”. I could also see
the debugging value in a “future” case label, but it’s not necessary IMO.

Although, I can kind of see your point:

If you’re talking about a private framework (e.g. the multi-module App
scenario), then things are a little different for you because typically,
you’ll ship new versions of the framework and App together. The framework
can be as fragile as you like in that case - you can break all the ABI
stability rules, because there will no apps importing the new version of
the framework who were compiled against older versions of it. In many ways,
those frameworks are really like static libraries (with extras, like
resources).

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.

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.

- Karl

Next off is developer usability. As someone who has contributed to several
frameworks in Swift, one thing I know is that you can always count on
people complaining about the usability of their library - it's something
you learn to expect. If it turns out that people are very frustrated with
an enum constantly changing and breaking compatibility, they will voice
that concern with a thousand :+1: on github, debate will happen, and the
appropriate course correction will be made. In this case, no real damage
done.

That said, if enums are nonexhaustive by default, frameworks will have
more nonexhaustive enums (as it becomes convention). The class of bugs
previously discussed will emerge, causing real damage to users and
developers. Complaints will arise, but with more hostility. In this case,
we end up back where we started, since framework developers will then mark
exhaustive for all their enums. The only difference is that it'll be
something that must be remembered to be done.

I can understand how someone developing an Apple framework may want
nonexhaustive by default, since enums from some Apple libraries seem to be
much larger and change relatively often. Yet, from my experience, this
doesn't represent what you find in open source libraries that seem to land
on something consistent and then stick with it. And that consistency pairs
very well with exhaustiveness.

Given this, it's clear that adding a case to an enum as a source breaking
change should be the expected behavior. It's safer for those using the
frameworks, and back propagation from users will correct it if it becomes
an annoyance. Nonexhaustive as a keyword is a nice additional tool to make
this correction simpler (as well as protect C enums by default), but should
not be the standard.

Best,
Rex

On Fri, Sep 15, 2017 at 5:06 PM, Jordan Rose <jordan_rose@apple.com> w
rote:

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 <rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/>
> FOLLOW US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>

--
Rex Fenley | IOS DEVELOPER

Remind.com <https://www.remind.com/> | BLOG <http://blog.remind.com/> |
FOLLOW US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--

Rex Fenley | IOS DEVELOPER

Remind.com <https://www.remind.com/> | BLOG <http://blog.remind.com/>
> FOLLOW
US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>

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.

Jessie

Hi Jordan,

I've been keeping up with most of the discussion since I last emailed about
my concern about making nonexhaustive the default mode for enums. So far I
am still strongly in the camp of exhaustive by default.

Most of my understanding comes from the perspective of source
compatibility; since I'm primarily an iOS app developer and contributor to
open source through CocoaPods, I don't have enough experience with binary
compatibility issues to understand the breadth of the effects of such a
change on binary compatibility - so I will argue from the perspective of
source compatibility. Additionally, all the following applies exclusively
to enums from Swift and not Obj-C/C. Largely my concerns stem from the "default
effect <https://en.m.wikipedia.org/wiki/Default_effect_(psychology)>"-
whatever default is chosen is going to unconsciously bias developers to use
that default.

To start, I don't follow how nonexhaustive by default leads to more secure
code or promises less. In a world where nonexhaustive is default,
hard-to-track bugs will be introduced frequently into many Swift
developer's code.

   - Evolving contracts - The change you suggest leaves it up to the user
   to remember to add new cases into their code if an enum ever does change in
   a framework, and their code relies on exhaustive pattern matching.

I think this is the right thing to do. Library vendors can still
communicate changes with clients through deprecation notices. Ultimately it
is up to the vendor to maintain compatibility for people switching against
those older cases, and up to the clients to adopt any new behaviour that
gets implemented.

   - Unhandled case bugs - This will lead to inconsistencies and
   hard-to-track bugs, since it would require the user to then track down an
   unhandled case that they may not even be aware exists (forcing googling,
   searching through documentation, etc.).

This is something that I certainly have experienced in the past working
across teams programming in Obj-C, and Swift has so far completely
eliminated this class of bugs! In a world where exhaustive remains default,
nothing changes, fewer bugs.

I’m not sure that those issues are so difficult to track down. You’ll know
that switch statements which fall-through to a default case are suspicious
- but they already are today, too!. It’s not really any different to having
allowing singular “if”s without a corresponding “else”. I could also see
the debugging value in a “future” case label, but it’s not necessary IMO.

Although, I can kind of see your point:

If you’re talking about a private framework (e.g. the multi-module App
scenario), then things are a little different for you because typically,
you’ll ship new versions of the framework and App together. The framework
can be as fragile as you like in that case - you can break all the ABI
stability rules, because there will no apps importing the new version of
the framework who were compiled against older versions of it. In many ways,
those frameworks are really like static libraries (with extras, like
resources).

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.

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.

- Karl

Next off is developer usability. As someone who has contributed to several
frameworks in Swift, one thing I know is that you can always count on
people complaining about the usability of their library - it's something
you learn to expect. If it turns out that people are very frustrated with
an enum constantly changing and breaking compatibility, they will voice
that concern with a thousand :+1: on github, debate will happen, and the
appropriate course correction will be made. In this case, no real damage
done.

That said, if enums are nonexhaustive by default, frameworks will have more
nonexhaustive enums (as it becomes convention). The class of bugs
previously discussed will emerge, causing real damage to users and
developers. Complaints will arise, but with more hostility. In this case,
we end up back where we started, since framework developers will then mark
exhaustive for all their enums. The only difference is that it'll be
something that must be remembered to be done.

I can understand how someone developing an Apple framework may want
nonexhaustive by default, since enums from some Apple libraries seem to be
much larger and change relatively often. Yet, from my experience, this
doesn't represent what you find in open source libraries that seem to land
on something consistent and then stick with it. And that consistency pairs
very well with exhaustiveness.

Given this, it's clear that adding a case to an enum as a source breaking
change should be the expected behavior. It's safer for those using the
frameworks, and back propagation from users will correct it if it becomes
an annoyance. Nonexhaustive as a keyword is a nice additional tool to make
this correction simpler (as well as protect C enums by default), but should
not be the standard.

Best,
Rex

···

On 21. Sep 2017, at 00:51, Rex Fenley via swift-evolution < swift-evolution@swift.org> wrote:

On Fri, Sep 15, 2017 at 5:06 PM, Jordan Rose <jordan_rose@apple.com> 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 <rex@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 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 <https://www.remind.com/> | BLOG <http://blog.remind.com/> |
FOLLOW US <https://twitter.com/remindhq> | LIKE US
<https://www.facebook.com/remindhq>

_______________________________________________

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

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.

···

On Sep 22, 2017, at 08:20, Karl Wagner <razielim@gmail.com> 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.

Nothing, other than it would break code written using version 1.

- Dave Sweeris

···

On Sep 20, 2017, at 4:15 PM, Dave DeLong via swift-evolution <swift-evolution@swift.org> 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?

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

···

On Sep 20, 2017, at 16:15, Dave DeLong <swift@davedelong.com> 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.

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

···

On Sep 27, 2017, at 16:48, Jessie Serrino <jessie@serrino.co> 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.

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 <https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md> 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 <http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0375r0.html> 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 <https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/signatures>. 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 <https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/enum#robust-programming> 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.

···

On Sep 22, 2017, at 17:50, Rex Fenley <rex@remind101.com> wrote:

---

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

I don’t think this use-case should be overlooked, especially as we approach the stable ABI.

If a library can change out from underneath you (because you’re not embedding it yourself) then it is inherently unsafe to assume that any enum in that library is exhaustive. The developer may think that it is for version 1, but the development of version 2 may reveal cases that were not originally considered. Apple, which has just about as much experience as anyone in the world with shipping frameworks, has a strong commitment to binary compatibility, but even they get it wrong sometimes.

IMO, the proper way to deal with “exhaustive” enums vs not is:

- any enum in any framework you ship is exhaustive. (Rationale: since you’re embedding it with your product, you have explicit control over handling all its cases)

- any enum in any framework you link is non-exhaustive. (Rationale: since the framework is not part of your product, it could change without you knowing, which means you must handle unexpected values via a default case)

Dave

···

On Sep 21, 2017, at 12:48 PM, Jordan Rose <jordan_rose@apple.com> wrote:

On Sep 20, 2017, at 16:15, Dave DeLong <swift@davedelong.com> 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

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.

Jon

···

On Sep 28, 2017, at 4:52 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 27, 2017, at 16:48, Jessie Serrino <jessie@serrino.co <mailto:jessie@serrino.co>> 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