SE-0192 — Non-Exhaustive Enums

I think there are a couple of different definitions running around, and that is confusing things.

In my mind, ‘unexpected:’ catches only cases which were unknown at compile time. Adding cases to an enum *should* be a source-breaking change. That is the whole point of this. We should have to update the switch (either by handling new case explicitly, or by adding default) before successfully compiling. What ‘unexpected:’ protects against are changes to a linked binary (e.g. iOS) that are now vending cases we didn’t know about when we were compiled.

I’ll say it again… framing this idea as one of exhaustiveness is really confusing. Enums should just always be exhaustive in swift. There may be cases where we need to use ‘unexpected:’ to handle unexpected/future cases exhaustively. If we annotate an enum as @frozen, then we won’t need to do that to be exhaustive because we know it won’t change out from under us. Always exhaustive. Much less confusing…

Thanks,
Jon

I think, then, you fundamentally disagree with the starting premise of the proposal, which is specifically the addition of nonexhaustive enums to the language, and making them the default sort of enum so that adding cases *is not* a source-breaking change. If your whole purpose is to change the proposal so that adding cases will _always_ be a source-breaking change and enums are _never_ nonexhaustive, then I'm not sure how to proceed in the discussion as we're working towards diametrically opposite goals.

The main issue is a resilience issue and it mainly affects binary frameworks the app links with at runtime and does not package them (read mostly Apple ones for the moment)... the fact that enum changes are source breaking is the whole entire point of swift’s enum and a best practice warning to always have turned on even before with Objective-C. Our goal, and yours goal should be the one and the same too IMHO, is not to throw the proverbial baby out with the bath water.

We should look for a solution that allows what Apple and OS framework authors need and also what is best for app developers and if we need something to do this we pause this proposal until that something is ready and merged.

···

On 3 Jan 2018, at 00:38, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Tue, Jan 2, 2018 at 4:31 PM, Jonathan Hull <jhull@gbis.com> wrote:

On Jan 2, 2018, at 1:41 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Tue, Jan 2, 2018 at 3:27 PM, Kevin Nattinger <swift@nattinger.net> wrote:
[...]

in what other circumstances do we insist that the compiler inform the end user about future additions to the API at compile time?

This isn’t a request for the compiler to inform the user about future additions to an API. It is a request to validate the compiler’s knowledge of the current state of an API with the current state of the source code.

Well, it's of course impossible to inform the user about future additions, so that's poorly phrased on my part. It's about the compiler informing the end user about *new* additions, part of the *current* state of the API, that have cropped up since the user last revised the code when the API was in a *previous* state (or, indistinguishably, members of which a user is unaware regardless of the temporal sequence of when such members were added). In what other circumstances do we insist that the compiler perform this service?

Enums. That's literally how they work today. You are arguing in favor of actively removing compiler-aided correctness.

There's also protocol requirements

No, that's now how enums work today, and it's not how protocol requirements work today. Enums today are all semantically exhaustive; if a case is added in a later version of a library, it's semantically a *different* enum type that happens to share the same name. Not considering all the cases of an exhaustive enum is an _error_, not a _warning_, because there is no basis on which to proceed. This will not change with the proposal. Likewise, adding a protocol requirement without a default implementation is source-breaking. The result is a compiler _error_.

The question is, what non-source breaking API additions today cause the compiler to inform the end user of such additions? The answer is: none whatsoever. Not new methods or properties on a type, not even new protocol requirements that have a default implementation.

and, arguably, deprecated methods with a proper message ("use foo instead").

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile time known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of “unknown case” giving you compile-time warnings for subsequently added cases, which was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out of scope for this proposal. <SE-0192 Non-exhaustive enums: add `unknown` and rename to '@frozen' by jrose-apple · Pull Request #777 · apple/swift-evolution · GitHub;

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all currently known cases during compile time? case * and case _ would be the same in exhaustive enums.

This is why I was suggesting another pattern that only captures known cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

···

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:
On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

As has already been said, “case unknown” is source-breaking because it conflicts with any real cases named “unknown”; “\unknown” looks like a key path but isn’t, and I wonder if it would potentially conflict with existing key paths.

In any case, my point was not to bikeshed the “unknown” part, but to ask whether any consideration had been made to have the feature presented as a flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default", can't be mixed with "default", and can't be used in case patterns, why not "default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md\]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile time
known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of “unknown
case” giving you compile-time warnings for subsequently added cases, which
was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out of
scope for this proposal.
<SE-0192 Non-exhaustive enums: add `unknown` and rename to '@frozen' by jrose-apple · Pull Request #777 · apple/swift-evolution · GitHub;

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all currently

known cases during compile time? case * and case _ would be the same in
exhaustive enums.

This is why I was suggesting another pattern that only captures known
cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

Sorry, I don’t understand. However you spell it, what is your use case for
this? The stated purpose of “unknown case” is to gain compile-time
exhaustiveness testing, but this would not allow for that.

···

On Thu, Jan 4, 2018 at 17:15 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com> wrote:

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As has already been said, “case unknown” is source-breaking because it
conflicts with any real cases named “unknown”; “\unknown” looks like a key
path but isn’t, and I wonder if it would potentially conflict with existing
key paths.

In any case, my point was not to bikeshed the “unknown” part, but to ask
whether any consideration had been made to have the feature presented as a
flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution < >>> swift-evolution@swift.org> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default", can't
be mixed with "default", and can't be used in case patterns, why not
"default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution < >>> swift-evolution@swift.org> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

At this point I think it might be useful to outline how binary compatibility works for Objective-C on Apple platforms right now. As an app developer I’m not intimately familiar with what happens when you run an app compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code paths or something else? The more this thread goes on the more confused I get about why Swift would have this issue while it doesn’t appear to be one for Obj-C. If an enum adds a case now, I don’t have to care until I recompile using the new SDK. Is the intention for Swift to be different in this regard?

Jon Shier

···

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

Is it hard to imagine that most everyone can get what they want and keep the syntax clean and streamlined at the same time? Without any "@" signs or other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user can be presented with migration options from an automated IDE tool. In what specific way does this not solve the issue about having to upgrade your code when using someone else's code library? This very notion implies your disgruntled about doing work when things are upgraded, is that really what this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need hints embedded in the code syntax itself. Migration hints from version to version should not be a part of either the past or future version of the code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated migration tool behind the scenes in the IDE to handle all your versioning worries, does not make this whole discussion about adding more convoluted syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

I think this whole thing has been unnecessarily convoluted. As a result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

A list of something is desired:
1 - Pancake
2 - Waffle
3 - Juice

Developer wishes to be able to:
A) Add new things to the list of choices in the future as they come up with new ideas
B) Sometimes select one of the choices to be chosen as the normal choice if no choice is made by the user

A and B are separate desires. In some circumstances a developer may want to add a new choice and make it the normal choice when there was no normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled here. Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions. There is little here to do with what a "default" should be. Indeed, it is an explicit design decision of Swift not to support types having an implicit default value.

____________________

Part 2:

After this simple desire is clear, there should be two discussions:
A) In a text only coding language, what would we like the syntax to look like? (Without regard to past-bias. What should it really be, forget what mistaken design choices were made in Swift in the past)
B) How do we approach making this happen behind the scenes?

Bonus: Given that some of us have changed our approach to programming significantly beyond text based coding, and into more dynamic mediums of programming in other niches, and even here and there in Xcode - I would recommend considering how the IDE would show a modern version of this concept. I feel too often that Swift design syntax has a lack of awareness between the distinctions of what the IDE should do, as opposed to what the syntax of the language should be, and what should be handled behind the scenes by automated tooling.

_____________________

My opinion, in answering the above questions is in preference to a simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, Waffle, Juice
}

If a "default" choice is desired, it is obvious to me that I would select the choice from the IDE, and it would be visually indicated that it was the default.

When changes occur, whether new choices are added, old ones are removed or changed, or a default is added, changed, or removed - a behind the scenes automated tool analyzes the changes and presents migration options through the IDE.

_____________________

Sincerely,
Jason

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

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

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

I think there are a couple of different definitions running around, and
that is confusing things.

In my mind, ‘unexpected:’ catches only cases which were unknown at
compile time. Adding cases to an enum *should* be a source-breaking change.
That is the whole point of this. We should have to update the switch
(either by handling new case explicitly, or by adding default) before
successfully compiling. What ‘unexpected:’ protects against are changes to
a linked binary (e.g. iOS) that are now vending cases we didn’t know about
when we were compiled.

I’ll say it again… framing this idea as one of exhaustiveness is really
confusing. Enums should just always be exhaustive in swift. There may be
cases where we need to use ‘unexpected:’ to handle unexpected/future cases
exhaustively. If we annotate an enum as @frozen, then we won’t need to do
that to be exhaustive because we know it won’t change out from under us.
Always exhaustive. Much less confusing…

Thanks,
Jon

I think, then, you fundamentally disagree with the starting premise of the
proposal, which is specifically the addition of nonexhaustive enums to the
language, and making them the default sort of enum so that adding cases *is
not* a source-breaking change. If your whole purpose is to change the
proposal so that adding cases will _always_ be a source-breaking change and
enums are _never_ nonexhaustive, then I'm not sure how to proceed in the
discussion as we're working towards diametrically opposite goals.

The main issue is a resilience issue and it mainly affects binary
frameworks the app links with at runtime and does not package them (read
mostly Apple ones for the moment)... the fact that enum changes are source
breaking is the whole entire point of swift’s enum and a best practice
warning to always have turned on even before with Objective-C. Our goal,
and yours goal should be the one and the same too IMHO, is not to throw the
proverbial baby out with the bath water.

We should look for a solution that allows what Apple and OS framework
authors need and also what is best for app developers and if we need
something to do this we pause this proposal until that something is ready
and merged.

Hmm, I think indeed we disagree fundamentally on what this proposal is
about, or the desired end goal.

If I understand correctly, *your* end goal is simply to enable something
like `@_fixed_layout` for enums, an optimization hint to allow binary
frameworks to add cases without being shipped with apps. You want no
semantic changes to how enums work.

*My* understand of this proposals goal--and I support it--is that it takes
this ABI compatibility problem as a starting point and discovers that there
are fundamentally two semantic categories of enums, ones that are
necessarily exhaustive (there's either something or nothing for an Optional
value, there are three possible comparison results for comparable types,
there are four sides to a rectangular window, etc.) and ones that are not.
It discovers that, empirically, most enums are nonexhaustive, and proposes
changes to the grammar so that Swift distinguishes between these two so as
to prompt developers to deal with the issue of nonexhaustiveness where the
semantics require it. The goal, then, is to introduce new spellings and
semantics to distinguish between two different flavors of enum. Like `open`
vs. `public` for classes, this has implications for ABI compatibility but
isn't just about ABI compatibility.

I was laboring under the idea that we were working to make such a
wide-ranging change as ergonomic as possible, but it seems like you and Jon
do not want such a change at all.

···

On Tue, Jan 2, 2018 at 7:02 PM, Goffredo Marocchi <panajev@gmail.com> wrote:

On 3 Jan 2018, at 00:38, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
On Tue, Jan 2, 2018 at 4:31 PM, Jonathan Hull <jhull@gbis.com> wrote:

On Jan 2, 2018, at 1:41 PM, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

On Tue, Jan 2, 2018 at 3:27 PM, Kevin Nattinger <swift@nattinger.net> >> wrote:

[...]

in what other circumstances do we insist that the compiler inform the

end user about future additions to the API at compile time?

This isn’t a request for the compiler to inform the user about future
additions to an API. It is a request to validate the compiler’s knowledge
of the *current* state of an API with the *current* state of the
source code.

Well, it's of course impossible to inform the user about future
additions, so that's poorly phrased on my part. It's about the compiler
informing the end user about *new* additions, part of the *current* state
of the API, that have cropped up since the user last revised the code when
the API was in a *previous* state (or, indistinguishably, members of which
a user is unaware regardless of the temporal sequence of when such members
were added). In what other circumstances do we insist that the compiler
perform this service?

Enums. That's literally how they work today. You are arguing in favor of
actively removing compiler-aided correctness.

There's also protocol requirements

No, that's now how enums work today, and it's not how protocol
requirements work today. Enums today are all semantically exhaustive; if a
case is added in a later version of a library, it's semantically a
*different* enum type that happens to share the same name. Not considering
all the cases of an exhaustive enum is an _error_, not a _warning_, because
there is no basis on which to proceed. This will not change with the
proposal. Likewise, adding a protocol requirement without a default
implementation is source-breaking. The result is a compiler _error_.

The question is, what non-source breaking API additions today cause the
compiler to inform the end user of such additions? The answer is: none
whatsoever. Not new methods or properties on a type, not even new protocol
requirements that have a default implementation.

and, arguably, deprecated methods with a proper message ("use foo

instead").

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

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

This is not what I am saying.

Change X helps use case A, but unnecessarily removes feature important (and like argument labels for everything quite Swift defining, but alas...) for use case B.

What I am saying is that before merging change X we should figure out what is needed (change Y) to ensure that use case B is not harmed while we do also help use case A.

Use case A being binary frameworks shipped by the OS and use case B being everything else... :).

Change X being the current proposal.

Change Y being the feature(s) needed to be added to X to ensure X’ helps use case A without removing the functionality use case B relies on.

···

Sent from my iPhone

On 3 Jan 2018, at 02:01, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Jan 2, 2018 at 7:02 PM, Goffredo Marocchi <panajev@gmail.com> wrote:

On 3 Jan 2018, at 00:38, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Tue, Jan 2, 2018 at 4:31 PM, Jonathan Hull <jhull@gbis.com> wrote:
I think there are a couple of different definitions running around, and that is confusing things.

In my mind, ‘unexpected:’ catches only cases which were unknown at compile time. Adding cases to an enum *should* be a source-breaking change. That is the whole point of this. We should have to update the switch (either by handling new case explicitly, or by adding default) before successfully compiling. What ‘unexpected:’ protects against are changes to a linked binary (e.g. iOS) that are now vending cases we didn’t know about when we were compiled.

I’ll say it again… framing this idea as one of exhaustiveness is really confusing. Enums should just always be exhaustive in swift. There may be cases where we need to use ‘unexpected:’ to handle unexpected/future cases exhaustively. If we annotate an enum as @frozen, then we won’t need to do that to be exhaustive because we know it won’t change out from under us. Always exhaustive. Much less confusing…

Thanks,
Jon

I think, then, you fundamentally disagree with the starting premise of the proposal, which is specifically the addition of nonexhaustive enums to the language, and making them the default sort of enum so that adding cases *is not* a source-breaking change. If your whole purpose is to change the proposal so that adding cases will _always_ be a source-breaking change and enums are _never_ nonexhaustive, then I'm not sure how to proceed in the discussion as we're working towards diametrically opposite goals.

The main issue is a resilience issue and it mainly affects binary frameworks the app links with at runtime and does not package them (read mostly Apple ones for the moment)... the fact that enum changes are source breaking is the whole entire point of swift’s enum and a best practice warning to always have turned on even before with Objective-C. Our goal, and yours goal should be the one and the same too IMHO, is not to throw the proverbial baby out with the bath water.

We should look for a solution that allows what Apple and OS framework authors need and also what is best for app developers and if we need something to do this we pause this proposal until that something is ready and merged.

Hmm, I think indeed we disagree fundamentally on what this proposal is about, or the desired end goal.

If I understand correctly, *your* end goal is simply to enable something like `@_fixed_layout` for enums, an optimization hint to allow binary frameworks to add cases without being shipped with apps. You want no semantic changes to how enums work.

*My* understand of this proposals goal--and I support it--is that it takes this ABI compatibility problem as a starting point and discovers that there are fundamentally two semantic categories of enums, ones that are necessarily exhaustive (there's either something or nothing for an Optional value, there are three possible comparison results for comparable types, there are four sides to a rectangular window, etc.) and ones that are not. It discovers that, empirically, most enums are nonexhaustive, and proposes changes to the grammar so that Swift distinguishes between these two so as to prompt developers to deal with the issue of nonexhaustiveness where the semantics require it. The goal, then, is to introduce new spellings and semantics to distinguish between two different flavors of enum. Like `open` vs. `public` for classes, this has implications for ABI compatibility but isn't just about ABI compatibility.

I was laboring under the idea that we were working to make such a wide-ranging change as ergonomic as possible, but it seems like you and Jon do not want such a change at all.

On Jan 2, 2018, at 1:41 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

On Tue, Jan 2, 2018 at 3:27 PM, Kevin Nattinger <swift@nattinger.net> wrote:
[...]

in what other circumstances do we insist that the compiler inform the end user about future additions to the API at compile time?

This isn’t a request for the compiler to inform the user about future additions to an API. It is a request to validate the compiler’s knowledge of the current state of an API with the current state of the source code.

Well, it's of course impossible to inform the user about future additions, so that's poorly phrased on my part. It's about the compiler informing the end user about *new* additions, part of the *current* state of the API, that have cropped up since the user last revised the code when the API was in a *previous* state (or, indistinguishably, members of which a user is unaware regardless of the temporal sequence of when such members were added). In what other circumstances do we insist that the compiler perform this service?

Enums. That's literally how they work today. You are arguing in favor of actively removing compiler-aided correctness.

There's also protocol requirements

No, that's now how enums work today, and it's not how protocol requirements work today. Enums today are all semantically exhaustive; if a case is added in a later version of a library, it's semantically a *different* enum type that happens to share the same name. Not considering all the cases of an exhaustive enum is an _error_, not a _warning_, because there is no basis on which to proceed. This will not change with the proposal. Likewise, adding a protocol requirement without a default implementation is source-breaking. The result is a compiler _error_.

The question is, what non-source breaking API additions today cause the compiler to inform the end user of such additions? The answer is: none whatsoever. Not new methods or properties on a type, not even new protocol requirements that have a default implementation.

and, arguably, deprecated methods with a proper message ("use foo instead").

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile time known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of “unknown case” giving you compile-time warnings for subsequently added cases, which was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out of scope for this proposal. <SE-0192 Non-exhaustive enums: add `unknown` and rename to '@frozen' by jrose-apple · Pull Request #777 · apple/swift-evolution · GitHub;

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all currently known cases during compile time? case * and case _ would be the same in exhaustive enums.

This is why I was suggesting another pattern that only captures known cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

Sorry, I don’t understand. However you spell it, what is your use case for this? The stated purpose of “unknown case” is to gain compile-time exhaustiveness testing, but this would not allow for that.

switch (excuse, notifiedTeacherBeforeDeadline) {
case (.eatenByPet, true):
  // …
case (.thoughtItWasDueNextWeek, true):
  // …
case (unknown case, true):
  // …
case (_, false):
  // …
}

Im referring to the future direction section in the new PR <https://github.com/jrose-apple/swift-evolution/blob/6061c01fb4a6d742ba7213f46979c9b82891fc14/proposals/0192-non-exhaustive-enums.md#future-directions&gt;\. The above example if from there.

I am fine with `unknown case` being required to be at the end of the switch for now.

I think of `unknown case` as a pattern that only matches unknown cases no matter where on the switch it is.

This is why I do not think that `default unknown` would work well once `unknown case` can be used a pattern.

We can start a new thread on this if you’d like.

···

On Jan 4, 2018, at 2:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 17:15 Cheyo J. Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:
On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

As has already been said, “case unknown” is source-breaking because it conflicts with any real cases named “unknown”; “\unknown” looks like a key path but isn’t, and I wonder if it would potentially conflict with existing key paths.

In any case, my point was not to bikeshed the “unknown” part, but to ask whether any consideration had been made to have the feature presented as a flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default", can't be mixed with "default", and can't be used in case patterns, why not "default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md\]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile time
known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of
“unknown case” giving you compile-time warnings for subsequently added
cases, which was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out
of scope for this proposal.
<SE-0192 Non-exhaustive enums: add `unknown` and rename to '@frozen' by jrose-apple · Pull Request #777 · apple/swift-evolution · GitHub;

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all currently

known cases during compile time? case * and case _ would be the same in
exhaustive enums.

This is why I was suggesting another pattern that only captures known
cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

Sorry, I don’t understand. However you spell it, what is your use case for
this? The stated purpose of “unknown case” is to gain compile-time
exhaustiveness testing, but this would not allow for that.

switch (excuse, notifiedTeacherBeforeDeadline) {case (.eatenByPet, true):
  // …case (.thoughtItWasDueNextWeek, true):
  // …case (unknown case, true):
  // …case (_, false):
  // …}

Im referring to the future direction section in the new PR
<https://github.com/jrose-apple/swift-evolution/blob/6061c01fb4a6d742ba7213f46979c9b82891fc14/proposals/0192-non-exhaustive-enums.md#future-directions&gt;\.
The above example if from there.

I am fine with `unknown case` being required to be at the end of the
switch for now.

I think of `unknown case` as a pattern that only matches unknown cases no
matter where on the switch it is.

This is why I do not think that `default unknown` would work well once
`unknown case` can be used a pattern.

We can start a new thread on this if you’d like.

The reason I put forward “default unknown” is precisely because the
proposed feature *cannot* be used in a pattern and therefore seems more apt
as not a case.

It actually makes it more natural to use in the given example above because
“default unknown” could actually be used to provide compile-time
exhaustiveness checking for such a tuple pattern, whereas without being
able to use “unknown case” in a pattern you can’t write “case (unknown
case, _)”.

You still haven’t answered my question, though—what’s the use case for the
feature you propose?

···

On Thu, Jan 4, 2018 at 18:39 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 2:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 17:15 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com> wrote:

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As has already been said, “case unknown” is source-breaking because it
conflicts with any real cases named “unknown”; “\unknown” looks like a key
path but isn’t, and I wonder if it would potentially conflict with existing
key paths.

In any case, my point was not to bikeshed the “unknown” part, but to ask
whether any consideration had been made to have the feature presented as a
flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution < >>>> swift-evolution@swift.org> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default", can't
be mixed with "default", and can't be used in case patterns, why not
"default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution < >>>> swift-evolution@swift.org> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

(Disclaimer: I’m no expert in this, and I know there are people in this list that are, so if there’s anything wrong please feel free to step in and correct me)

As far as I’m aware, Apple’s frameworks check which version of the framework you linked with at runtime (for UIKit, I believe this function is UIApplicationLinkedOnOrAfter) and modify their behavior to match. For example, if this function shows your application was linked with the iOS 6 SDK you’d get the old skeuomorphic UI instead of the “flatter”, newer iOS 7-style controls. What this means is every library actually keeps old implementations around in addition to the newer ones and picks the necessary one at runtime, so binary compatibility boils down to things like “don't change the memory layout of classes” rather than “don’t change the behavior of your program”.

Saagar Jha

···

On Jan 5, 2018, at 19:11, Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:

At this point I think it might be useful to outline how binary compatibility works for Objective-C on Apple platforms right now. As an app developer I’m not intimately familiar with what happens when you run an app compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code paths or something else? The more this thread goes on the more confused I get about why Swift would have this issue while it doesn’t appear to be one for Obj-C. If an enum adds a case now, I don’t have to care until I recompile using the new SDK. Is the intention for Swift to be different in this regard?

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Is it hard to imagine that most everyone can get what they want and keep the syntax clean and streamlined at the same time? Without any "@" signs or other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user can be presented with migration options from an automated IDE tool. In what specific way does this not solve the issue about having to upgrade your code when using someone else's code library? This very notion implies your disgruntled about doing work when things are upgraded, is that really what this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need hints embedded in the code syntax itself. Migration hints from version to version should not be a part of either the past or future version of the code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated migration tool behind the scenes in the IDE to handle all your versioning worries, does not make this whole discussion about adding more convoluted syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I think this whole thing has been unnecessarily convoluted. As a result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

A list of something is desired:
1 - Pancake
2 - Waffle
3 - Juice

Developer wishes to be able to:
A) Add new things to the list of choices in the future as they come up with new ideas
B) Sometimes select one of the choices to be chosen as the normal choice if no choice is made by the user

A and B are separate desires. In some circumstances a developer may want to add a new choice and make it the normal choice when there was no normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled here. Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions. There is little here to do with what a "default" should be. Indeed, it is an explicit design decision of Swift not to support types having an implicit default value.

____________________

Part 2:

After this simple desire is clear, there should be two discussions:
A) In a text only coding language, what would we like the syntax to look like? (Without regard to past-bias. What should it really be, forget what mistaken design choices were made in Swift in the past)
B) How do we approach making this happen behind the scenes?

Bonus: Given that some of us have changed our approach to programming significantly beyond text based coding, and into more dynamic mediums of programming in other niches, and even here and there in Xcode - I would recommend considering how the IDE would show a modern version of this concept. I feel too often that Swift design syntax has a lack of awareness between the distinctions of what the IDE should do, as opposed to what the syntax of the language should be, and what should be handled behind the scenes by automated tooling.

_____________________

My opinion, in answering the above questions is in preference to a simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, Waffle, Juice
}

If a "default" choice is desired, it is obvious to me that I would select the choice from the IDE, and it would be visually indicated that it was the default.

When changes occur, whether new choices are added, old ones are removed or changed, or a default is added, changed, or removed - a behind the scenes automated tool analyzes the changes and presents migration options through the IDE.

_____________________

Sincerely,
Jason

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

This is very much an issue in Obj-C today. If you have an NS_ENUM defined
with cases A, B, and C, this switch is correct:

int foo;
swith (e) {
case A: foo = 0; break;
case B: foo = 1; break;
case C: foo = 2; break;
}

(Note the lack of a default case)

If that enum is defined in a framework and it changes after the app is
compiled (like it's the case with Apple frameworks), then that code
produces no warning, yet the foo variable will have a garbage value
(undefined behavior, but as far as the compiler can tell at compile time
your code is fine)

Adding a default clause to that switch has the downside of not getting
warnings for new added cases, like has been discussed before, which is very
useful.

···

On Fri, Jan 5, 2018 at 7:11 PM Jon Shier via swift-evolution < swift-evolution@swift.org> wrote:

At this point I think it might be useful to outline how binary
compatibility works for Objective-C on Apple platforms right now. As an app
developer I’m not intimately familiar with what happens when you run an app
compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to
call old code paths or something else? The more this thread goes on the
more confused I get about why Swift would have this issue while it doesn’t
appear to be one for Obj-C. If an enum adds a case now, I don’t have to
care until I recompile using the new SDK. Is the intention for Swift to be
different in this regard?

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution < > swift-evolution@swift.org> wrote:

Is it hard to imagine that most everyone can get what they want and keep
the syntax clean and streamlined at the same time? Without any "@" signs or
other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add

new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user
can be presented with migration options from an automated IDE tool. In what
specific way does this not solve the issue about having to upgrade your
code when using someone else's code library? This very notion implies your
disgruntled about doing work when things are upgraded, is that really what
this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need
hints embedded in the code syntax itself. Migration hints from version to
version should not be a part of either the past or future version of the
code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common
sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated
migration tool behind the scenes in the IDE to handle all your versioning
worries, does not make this whole discussion about adding more convoluted
syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution < >> swift-evolution@swift.org> wrote:

I think this whole thing has been unnecessarily convoluted. As a result,
the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

*A list of something is desired:*
1 - Pancake
2 - Waffle
3 - Juice

*Developer wishes to be able to:*
*A)* Add new things to the list of choices in the future as they come
up with new ideas
*B)* Sometimes select one of the choices to be chosen as the normal
choice if no choice is made by the user

A and B are *separate desires*. In some circumstances a developer may
want to add a new choice and make it the normal choice when there was no
normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled
here. Rather, we are how to enable the vendor of a nonexhaustive enum to
add new cases without breaking binaries compiled against previous versions.
There is little here to do with what a "default" should be. Indeed, it is
an explicit design decision of Swift not to support types having an
implicit default value.

____________________

*Part 2:*

After this simple desire is clear, there should be two discussions:
*A)* In a text only coding language, what would we like the syntax to
look like? (Without regard to past-bias. What should it really be, forget
what mistaken design choices were made in Swift in the past)
*B)* How do we approach making this happen behind the scenes?

*Bonus:* Given that some of us have changed our approach to programming
significantly beyond text based coding, and into more dynamic mediums of
programming in other niches, and even here and there in Xcode - I would
recommend considering how the IDE would show a modern version of this
concept. I feel too often that Swift design syntax has a *lack of
awareness between the distinctions of what the IDE should do, as opposed to
what the syntax of the language should be*, and what should be handled
behind the scenes by automated tooling.

_____________________

*My opinion*, in answering the above questions is in preference to a
simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, *Waffle*, Juice
}

If a "default" choice is desired, it is obvious to me that I would
select the choice from the IDE, and it would be visually indicated that it
was the default.

When changes occur, whether new choices are added, old ones are removed
or changed, or a default is added, changed, or removed - a behind the
scenes automated tool analyzes the changes and presents migration options
through the IDE.

_____________________

Sincerely,
Jason

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

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

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

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

--
Javier Soto

At this point I think it might be useful to outline how binary compatibility works for Objective-C on Apple platforms right now. As an app developer I’m not intimately familiar with what happens when you run an app compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code paths or something else? The more this thread goes on the more confused I get about why Swift would have this issue while it doesn’t appear to be one for Obj-C. If an enum adds a case now, I don’t have to care until I recompile using the new SDK. Is the intention for Swift to be different in this regard?

Obj-C does not mandate that you have to handle all cases in an enum. Swift does. In Obj-C, when you switch over an unhandled value, the code just flow after the switch and continue executing. Your code may be silently buggy (as it misses a case), but it does not crash.

In Swift, if you swift over an unhandled case, the program abort execution. This is part of the Swift safety model where the language prefers to crash soon than continuing with potentially broken assumptions and crashing later at a completely unrelated point (which make it very hard to find the root cause of the crash).

···

Le 6 janv. 2018 à 04:11, Jon Shier via swift-evolution <swift-evolution@swift.org> a écrit :

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Is it hard to imagine that most everyone can get what they want and keep the syntax clean and streamlined at the same time? Without any "@" signs or other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user can be presented with migration options from an automated IDE tool. In what specific way does this not solve the issue about having to upgrade your code when using someone else's code library? This very notion implies your disgruntled about doing work when things are upgraded, is that really what this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need hints embedded in the code syntax itself. Migration hints from version to version should not be a part of either the past or future version of the code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated migration tool behind the scenes in the IDE to handle all your versioning worries, does not make this whole discussion about adding more convoluted syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I think this whole thing has been unnecessarily convoluted. As a result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

A list of something is desired:
1 - Pancake
2 - Waffle
3 - Juice

Developer wishes to be able to:
A) Add new things to the list of choices in the future as they come up with new ideas
B) Sometimes select one of the choices to be chosen as the normal choice if no choice is made by the user

A and B are separate desires. In some circumstances a developer may want to add a new choice and make it the normal choice when there was no normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled here. Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions. There is little here to do with what a "default" should be. Indeed, it is an explicit design decision of Swift not to support types having an implicit default value.

____________________

Part 2:

After this simple desire is clear, there should be two discussions:
A) In a text only coding language, what would we like the syntax to look like? (Without regard to past-bias. What should it really be, forget what mistaken design choices were made in Swift in the past)
B) How do we approach making this happen behind the scenes?

Bonus: Given that some of us have changed our approach to programming significantly beyond text based coding, and into more dynamic mediums of programming in other niches, and even here and there in Xcode - I would recommend considering how the IDE would show a modern version of this concept. I feel too often that Swift design syntax has a lack of awareness between the distinctions of what the IDE should do, as opposed to what the syntax of the language should be, and what should be handled behind the scenes by automated tooling.

_____________________

My opinion, in answering the above questions is in preference to a simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, Waffle, Juice
}

If a "default" choice is desired, it is obvious to me that I would select the choice from the IDE, and it would be visually indicated that it was the default.

When changes occur, whether new choices are added, old ones are removed or changed, or a default is added, changed, or removed - a behind the scenes automated tool analyzes the changes and presents migration options through the IDE.

_____________________

Sincerely,
Jason

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile time known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of “unknown case” giving you compile-time warnings for subsequently added cases, which was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out of scope for this proposal. <SE-0192 Non-exhaustive enums: add `unknown` and rename to '@frozen' by jrose-apple · Pull Request #777 · apple/swift-evolution · GitHub;

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all currently known cases during compile time? case * and case _ would be the same in exhaustive enums.

This is why I was suggesting another pattern that only captures known cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

Sorry, I don’t understand. However you spell it, what is your use case for this? The stated purpose of “unknown case” is to gain compile-time exhaustiveness testing, but this would not allow for that.

switch (excuse, notifiedTeacherBeforeDeadline) {
case (.eatenByPet, true):
  // …
case (.thoughtItWasDueNextWeek, true):
  // …
case (unknown case, true):
  // …
case (_, false):
  // …
}

Im referring to the future direction section in the new PR <https://github.com/jrose-apple/swift-evolution/blob/6061c01fb4a6d742ba7213f46979c9b82891fc14/proposals/0192-non-exhaustive-enums.md#future-directions&gt;\. The above example if from there.

I am fine with `unknown case` being required to be at the end of the switch for now.

I think of `unknown case` as a pattern that only matches unknown cases no matter where on the switch it is.

This is why I do not think that `default unknown` would work well once `unknown case` can be used a pattern.

We can start a new thread on this if you’d like.

The reason I put forward “default unknown” is precisely because the proposed feature *cannot* be used in a pattern and therefore seems more apt as not a case.

It can not be used in a pattern now but you could in the future if left as `case`.

It actually makes it more natural to use in the given example above because “default unknown” could actually be used to provide compile-time exhaustiveness checking for such a tuple pattern, whereas without being able to use “unknown case” in a pattern you can’t write “case (unknown case, _)”.

The way `unknown case` enforces compile-time exhaustiveness is by only matching unknown cases. The implementation may be more close to default by the virtue of being forced to go at the end of the switch statement now but that should not dictate the user experience.

You still haven’t answered my question, though—what’s the use case for the feature you propose?

My use case would be distinguishing between compile time known cases vs “future only” cases (or unknown cases). This depends on generalized `unknown case` patterns which is out of scope. I am happy to talk more about this on a different thread when this proposal gets approved.

···

On Jan 4, 2018, at 3:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 18:39 Cheyo J. Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:

On Jan 4, 2018, at 2:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Thu, Jan 4, 2018 at 17:15 Cheyo J. Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:
On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

As has already been said, “case unknown” is source-breaking because it conflicts with any real cases named “unknown”; “\unknown” looks like a key path but isn’t, and I wonder if it would potentially conflict with existing key paths.

In any case, my point was not to bikeshed the “unknown” part, but to ask whether any consideration had been made to have the feature presented as a flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com <mailto:cheyo@masters3d.com>> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default", can't be mixed with "default", and can't be used in case patterns, why not "default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md\]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile
time known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of
“unknown case” giving you compile-time warnings for subsequently added
cases, which was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out
of scope for this proposal.
<SE-0192 Non-exhaustive enums: add `unknown` and rename to '@frozen' by jrose-apple · Pull Request #777 · apple/swift-evolution · GitHub;

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all currently

known cases during compile time? case * and case _ would be the same in
exhaustive enums.

This is why I was suggesting another pattern that only captures known
cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

Sorry, I don’t understand. However you spell it, what is your use case
for this? The stated purpose of “unknown case” is to gain compile-time
exhaustiveness testing, but this would not allow for that.

switch (excuse, notifiedTeacherBeforeDeadline) {case (.eatenByPet, true):
  // …case (.thoughtItWasDueNextWeek, true):
  // …case (unknown case, true):
  // …case (_, false):
  // …}

Im referring to the future direction section in the new PR
<https://github.com/jrose-apple/swift-evolution/blob/6061c01fb4a6d742ba7213f46979c9b82891fc14/proposals/0192-non-exhaustive-enums.md#future-directions&gt;\.
The above example if from there.

I am fine with `unknown case` being required to be at the end of the
switch for now.

I think of `unknown case` as a pattern that only matches unknown cases no
matter where on the switch it is.

This is why I do not think that `default unknown` would work well once
`unknown case` can be used a pattern.

We can start a new thread on this if you’d like.

The reason I put forward “default unknown” is precisely because the
proposed feature *cannot* be used in a pattern and therefore seems more apt
as not a case.

It can not be used in a pattern now but you could in the future if left as
`case`.

It actually makes it more natural to use in the given example above
because “default unknown” could actually be used to provide compile-time
exhaustiveness checking for such a tuple pattern, whereas without being
able to use “unknown case” in a pattern you can’t write “case (unknown
case, _)”.

The way `unknown case` enforces compile-time exhaustiveness is by only
matching unknown cases. The implementation may be more close to default by
the virtue of being forced to go at the end of the switch statement now but
that should not dictate the user experience.

We seem to agree that, by virtue of not supporting use in a pattern and
being placed at the end, the feature is a flavor of default. I’m still not
sure I understand why you believe it should not be a flavor of default
going forward.

You still haven’t answered my question, though—what’s the use case for the
feature you propose?

My use case would be distinguishing between compile time known cases vs
“future only” cases (or unknown cases).

I understand that the feature you propose would allow you to make such a
distinction, but again, what is your use case for doing so?

This depends on generalized `unknown case` patterns which is out of scope.

···

On Thu, Jan 4, 2018 at 19:29 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 3:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 18:39 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 2:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 17:15 Cheyo J. Jimenez <cheyo@masters3d.com> >> wrote:

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com> wrote:

I am happy to talk more about this on a different thread when this proposal
gets approved.

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As has already been said, “case unknown” is source-breaking because it
conflicts with any real cases named “unknown”; “\unknown” looks like a key
path but isn’t, and I wonder if it would potentially conflict with existing
key paths.

In any case, my point was not to bikeshed the “unknown” part, but to
ask whether any consideration had been made to have the feature presented
as a flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default",
can't be mixed with "default", and can't be used in case patterns, why not
"default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

Except it clearly doesn’t happen today when Apple ships new SDKs. Obviously there’s an alternate mechanism used in that case. I’m just curious what it is and why Swift so desperately needs an alternative.

Jon

···

On Jan 6, 2018, at 5:49 PM, Javier Soto <javier.api@gmail.com> wrote:

This is very much an issue in Obj-C today. If you have an NS_ENUM defined with cases A, B, and C, this switch is correct:

int foo;
swith (e) {
case A: foo = 0; break;
case B: foo = 1; break;
case C: foo = 2; break;
}

(Note the lack of a default case)

If that enum is defined in a framework and it changes after the app is compiled (like it's the case with Apple frameworks), then that code produces no warning, yet the foo variable will have a garbage value (undefined behavior, but as far as the compiler can tell at compile time your code is fine)

Adding a default clause to that switch has the downside of not getting warnings for new added cases, like has been discussed before, which is very useful.

On Fri, Jan 5, 2018 at 7:11 PM Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:
At this point I think it might be useful to outline how binary compatibility works for Objective-C on Apple platforms right now. As an app developer I’m not intimately familiar with what happens when you run an app compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code paths or something else? The more this thread goes on the more confused I get about why Swift would have this issue while it doesn’t appear to be one for Obj-C. If an enum adds a case now, I don’t have to care until I recompile using the new SDK. Is the intention for Swift to be different in this regard?

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

Is it hard to imagine that most everyone can get what they want and keep the syntax clean and streamlined at the same time? Without any "@" signs or other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user can be presented with migration options from an automated IDE tool. In what specific way does this not solve the issue about having to upgrade your code when using someone else's code library? This very notion implies your disgruntled about doing work when things are upgraded, is that really what this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need hints embedded in the code syntax itself. Migration hints from version to version should not be a part of either the past or future version of the code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated migration tool behind the scenes in the IDE to handle all your versioning worries, does not make this whole discussion about adding more convoluted syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

I think this whole thing has been unnecessarily convoluted. As a result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

A list of something is desired:
1 - Pancake
2 - Waffle
3 - Juice

Developer wishes to be able to:
A) Add new things to the list of choices in the future as they come up with new ideas
B) Sometimes select one of the choices to be chosen as the normal choice if no choice is made by the user

A and B are separate desires. In some circumstances a developer may want to add a new choice and make it the normal choice when there was no normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled here. Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions. There is little here to do with what a "default" should be. Indeed, it is an explicit design decision of Swift not to support types having an implicit default value.

____________________

Part 2:

After this simple desire is clear, there should be two discussions:
A) In a text only coding language, what would we like the syntax to look like? (Without regard to past-bias. What should it really be, forget what mistaken design choices were made in Swift in the past)
B) How do we approach making this happen behind the scenes?

Bonus: Given that some of us have changed our approach to programming significantly beyond text based coding, and into more dynamic mediums of programming in other niches, and even here and there in Xcode - I would recommend considering how the IDE would show a modern version of this concept. I feel too often that Swift design syntax has a lack of awareness between the distinctions of what the IDE should do, as opposed to what the syntax of the language should be, and what should be handled behind the scenes by automated tooling.

_____________________

My opinion, in answering the above questions is in preference to a simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, Waffle, Juice
}

If a "default" choice is desired, it is obvious to me that I would select the choice from the IDE, and it would be visually indicated that it was the default.

When changes occur, whether new choices are added, old ones are removed or changed, or a default is added, changed, or removed - a behind the scenes automated tool analyzes the changes and presents migration options through the IDE.

_____________________

Sincerely,
Jason

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

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

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

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

--
Javier Soto

What doesn't happen today? The issue is not when they ship a new SDK: When
rebuilding your app against it, you'll get a warning for a missing case.
The problem is when running the app against a newer iOS version with a
newer version of the SDK where the enum has a new case.

···

On Sat, Jan 6, 2018 at 3:10 PM Jon Shier <jon@jonshier.com> wrote:

Except it clearly doesn’t happen today when Apple ships new SDKs.
Obviously there’s an alternate mechanism used in that case. I’m just
curious what it is and why Swift so desperately needs an alternative.

Jon

On Jan 6, 2018, at 5:49 PM, Javier Soto <javier.api@gmail.com> wrote:

This is very much an issue in Obj-C today. If you have an NS_ENUM defined
with cases A, B, and C, this switch is correct:

int foo;
swith (e) {
case A: foo = 0; break;
case B: foo = 1; break;
case C: foo = 2; break;
}

(Note the lack of a default case)

If that enum is defined in a framework and it changes after the app is
compiled (like it's the case with Apple frameworks), then that code
produces no warning, yet the foo variable will have a garbage value
(undefined behavior, but as far as the compiler can tell at compile time
your code is fine)

Adding a default clause to that switch has the downside of not getting
warnings for new added cases, like has been discussed before, which is very
useful.
On Fri, Jan 5, 2018 at 7:11 PM Jon Shier via swift-evolution < > swift-evolution@swift.org> wrote:

At this point I think it might be useful to outline how binary
compatibility works for Objective-C on Apple platforms right now. As an app
developer I’m not intimately familiar with what happens when you run an app
compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to
call old code paths or something else? The more this thread goes on the
more confused I get about why Swift would have this issue while it doesn’t
appear to be one for Obj-C. If an enum adds a case now, I don’t have to
care until I recompile using the new SDK. Is the intention for Swift to be
different in this regard?

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution < >> swift-evolution@swift.org> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution < >> swift-evolution@swift.org> wrote:

Is it hard to imagine that most everyone can get what they want and keep
the syntax clean and streamlined at the same time? Without any "@" signs or
other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add

new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user
can be presented with migration options from an automated IDE tool. In what
specific way does this not solve the issue about having to upgrade your
code when using someone else's code library? This very notion implies your
disgruntled about doing work when things are upgraded, is that really what
this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need
hints embedded in the code syntax itself. Migration hints from version to
version should not be a part of either the past or future version of the
code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common
sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated
migration tool behind the scenes in the IDE to handle all your versioning
worries, does not make this whole discussion about adding more convoluted
syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution < >>> swift-evolution@swift.org> wrote:

I think this whole thing has been unnecessarily convoluted. As a
result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

*A list of something is desired:*
1 - Pancake
2 - Waffle
3 - Juice

*Developer wishes to be able to:*
*A)* Add new things to the list of choices in the future as they come
up with new ideas
*B)* Sometimes select one of the choices to be chosen as the normal
choice if no choice is made by the user

A and B are *separate desires*. In some circumstances a developer may
want to add a new choice and make it the normal choice when there was no
normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled
here. Rather, we are how to enable the vendor of a nonexhaustive enum to
add new cases without breaking binaries compiled against previous versions.
There is little here to do with what a "default" should be. Indeed, it is
an explicit design decision of Swift not to support types having an
implicit default value.

____________________

*Part 2:*

After this simple desire is clear, there should be two discussions:
*A)* In a text only coding language, what would we like the syntax to
look like? (Without regard to past-bias. What should it really be, forget
what mistaken design choices were made in Swift in the past)
*B)* How do we approach making this happen behind the scenes?

*Bonus:* Given that some of us have changed our approach to
programming significantly beyond text based coding, and into more dynamic
mediums of programming in other niches, and even here and there in Xcode -
I would recommend considering how the IDE would show a modern version of
this concept. I feel too often that Swift design syntax has a *lack of
awareness between the distinctions of what the IDE should do, as opposed to
what the syntax of the language should be*, and what should be handled
behind the scenes by automated tooling.

_____________________

*My opinion*, in answering the above questions is in preference to a
simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, *Waffle*, Juice
}

If a "default" choice is desired, it is obvious to me that I would
select the choice from the IDE, and it would be visually indicated that it
was the default.

When changes occur, whether new choices are added, old ones are removed
or changed, or a default is added, changed, or removed - a behind the
scenes automated tool analyzes the changes and presents migration options
through the IDE.

_____________________

Sincerely,
Jason

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

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

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

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

--
Javier Soto

--

Javier Soto

It's true that Apple framework authors have this capability, known as a "linked-on-or-after check", but it has several caveats:

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

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

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

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

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

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

Jordan

···

On Jan 8, 2018, at 11:02, Saagar Jha via swift-evolution <swift-evolution@swift.org> wrote:

(Disclaimer: I’m no expert in this, and I know there are people in this list that are, so if there’s anything wrong please feel free to step in and correct me)

As far as I’m aware, Apple’s frameworks check which version of the framework you linked with at runtime (for UIKit, I believe this function is UIApplicationLinkedOnOrAfter) and modify their behavior to match. For example, if this function shows your application was linked with the iOS 6 SDK you’d get the old skeuomorphic UI instead of the “flatter”, newer iOS 7-style controls. What this means is every library actually keeps old implementations around in addition to the newer ones and picks the necessary one at runtime, so binary compatibility boils down to things like “don't change the memory layout of classes” rather than “don’t change the behavior of your program”.

Saagar Jha

On Jan 5, 2018, at 19:11, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

At this point I think it might be useful to outline how binary compatibility works for Objective-C on Apple platforms right now. As an app developer I’m not intimately familiar with what happens when you run an app compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code paths or something else? The more this thread goes on the more confused I get about why Swift would have this issue while it doesn’t appear to be one for Obj-C. If an enum adds a case now, I don’t have to care until I recompile using the new SDK. Is the intention for Swift to be different in this regard?

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Is it hard to imagine that most everyone can get what they want and keep the syntax clean and streamlined at the same time? Without any "@" signs or other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user can be presented with migration options from an automated IDE tool. In what specific way does this not solve the issue about having to upgrade your code when using someone else's code library? This very notion implies your disgruntled about doing work when things are upgraded, is that really what this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need hints embedded in the code syntax itself. Migration hints from version to version should not be a part of either the past or future version of the code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated migration tool behind the scenes in the IDE to handle all your versioning worries, does not make this whole discussion about adding more convoluted syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:
On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I think this whole thing has been unnecessarily convoluted. As a result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

A list of something is desired:
1 - Pancake
2 - Waffle
3 - Juice

Developer wishes to be able to:
A) Add new things to the list of choices in the future as they come up with new ideas
B) Sometimes select one of the choices to be chosen as the normal choice if no choice is made by the user

A and B are separate desires. In some circumstances a developer may want to add a new choice and make it the normal choice when there was no normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled here. Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions. There is little here to do with what a "default" should be. Indeed, it is an explicit design decision of Swift not to support types having an implicit default value.

____________________

Part 2:

After this simple desire is clear, there should be two discussions:
A) In a text only coding language, what would we like the syntax to look like? (Without regard to past-bias. What should it really be, forget what mistaken design choices were made in Swift in the past)
B) How do we approach making this happen behind the scenes?

Bonus: Given that some of us have changed our approach to programming significantly beyond text based coding, and into more dynamic mediums of programming in other niches, and even here and there in Xcode - I would recommend considering how the IDE would show a modern version of this concept. I feel too often that Swift design syntax has a lack of awareness between the distinctions of what the IDE should do, as opposed to what the syntax of the language should be, and what should be handled behind the scenes by automated tooling.

_____________________

My opinion, in answering the above questions is in preference to a simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, Waffle, Juice
}

If a "default" choice is desired, it is obvious to me that I would select the choice from the IDE, and it would be visually indicated that it was the default.

When changes occur, whether new choices are added, old ones are removed or changed, or a default is added, changed, or removed - a behind the scenes automated tool analyzes the changes and presents migration options through the IDE.

_____________________

Sincerely,
Jason

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <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

Add -Weverything and you can ensure you are switching exhaustively ;).

···

Sent from my iPhone

On 8 Jan 2018, at 19:02, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This is very much an issue in Obj-C today. If you have an NS_ENUM defined with cases A, B, and C, this switch is correct:

int foo;
swith (e) {
case A: foo = 0; break;
case B: foo = 1; break;
case C: foo = 2; break;
}

(Note the lack of a default case)

If that enum is defined in a framework and it changes after the app is compiled (like it's the case with Apple frameworks), then that code produces no warning, yet the foo variable will have a garbage value (undefined behavior, but as far as the compiler can tell at compile time your code is fine)

Adding a default clause to that switch has the downside of not getting warnings for new added cases, like has been discussed before, which is very useful.

On Fri, Jan 5, 2018 at 7:11 PM Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:
At this point I think it might be useful to outline how binary compatibility works for Objective-C on Apple platforms right now. As an app developer I’m not intimately familiar with what happens when you run an app compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code paths or something else? The more this thread goes on the more confused I get about why Swift would have this issue while it doesn’t appear to be one for Obj-C. If an enum adds a case now, I don’t have to care until I recompile using the new SDK. Is the intention for Swift to be different in this regard?

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

Is it hard to imagine that most everyone can get what they want and keep the syntax clean and streamlined at the same time? Without any "@" signs or other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user can be presented with migration options from an automated IDE tool. In what specific way does this not solve the issue about having to upgrade your code when using someone else's code library? This very notion implies your disgruntled about doing work when things are upgraded, is that really what this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need hints embedded in the code syntax itself. Migration hints from version to version should not be a part of either the past or future version of the code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated migration tool behind the scenes in the IDE to handle all your versioning worries, does not make this whole discussion about adding more convoluted syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

I think this whole thing has been unnecessarily convoluted. As a result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

A list of something is desired:
1 - Pancake
2 - Waffle
3 - Juice

Developer wishes to be able to:
A) Add new things to the list of choices in the future as they come up with new ideas
B) Sometimes select one of the choices to be chosen as the normal choice if no choice is made by the user

A and B are separate desires. In some circumstances a developer may want to add a new choice and make it the normal choice when there was no normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled here. Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions. There is little here to do with what a "default" should be. Indeed, it is an explicit design decision of Swift not to support types having an implicit default value.

____________________

Part 2:

After this simple desire is clear, there should be two discussions:
A) In a text only coding language, what would we like the syntax to look like? (Without regard to past-bias. What should it really be, forget what mistaken design choices were made in Swift in the past)
B) How do we approach making this happen behind the scenes?

Bonus: Given that some of us have changed our approach to programming significantly beyond text based coding, and into more dynamic mediums of programming in other niches, and even here and there in Xcode - I would recommend considering how the IDE would show a modern version of this concept. I feel too often that Swift design syntax has a lack of awareness between the distinctions of what the IDE should do, as opposed to what the syntax of the language should be, and what should be handled behind the scenes by automated tooling.

_____________________

My opinion, in answering the above questions is in preference to a simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, Waffle, Juice
}

If a "default" choice is desired, it is obvious to me that I would select the choice from the IDE, and it would be visually indicated that it was the default.

When changes occur, whether new choices are added, old ones are removed or changed, or a default is added, changed, or removed - a behind the scenes automated tool analyzes the changes and presents migration options through the IDE.

_____________________

Sincerely,
Jason

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

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

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

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile time known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of “unknown case” giving you compile-time warnings for subsequently added cases, which was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out of scope for this proposal.

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all currently known cases during compile time? case * and case _ would be the same in exhaustive enums.

This is why I was suggesting another pattern that only captures known cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

Sorry, I don’t understand. However you spell it, what is your use case for this? The stated purpose of “unknown case” is to gain compile-time exhaustiveness testing, but this would not allow for that.

switch (excuse, notifiedTeacherBeforeDeadline) {
case (.eatenByPet, true):
  // …
case (.thoughtItWasDueNextWeek, true):
  // …
case (unknown case, true):
  // …
case (_, false):
  // …
}

Im referring to the future direction section in the new PR. The above example if from there.

I am fine with `unknown case` being required to be at the end of the switch for now.

I think of `unknown case` as a pattern that only matches unknown cases no matter where on the switch it is.

This is why I do not think that `default unknown` would work well once `unknown case` can be used a pattern.

We can start a new thread on this if you’d like.

The reason I put forward “default unknown” is precisely because the proposed feature *cannot* be used in a pattern and therefore seems more apt as not a case.

It can not be used in a pattern now but you could in the future if left as `case`.

It actually makes it more natural to use in the given example above because “default unknown” could actually be used to provide compile-time exhaustiveness checking for such a tuple pattern, whereas without being able to use “unknown case” in a pattern you can’t write “case (unknown case, _)”.

The way `unknown case` enforces compile-time exhaustiveness is by only matching unknown cases. The implementation may be more close to default by the virtue of being forced to go at the end of the switch statement now but that should not dictate the user experience.

We seem to agree that, by virtue of not supporting use in a pattern and being placed at the end, the feature is a flavor of default. I’m still not sure I understand why you believe it should not be a flavor of default going forward.

You still haven’t answered my question, though—what’s the use case for the feature you propose?

My use case would be distinguishing between compile time known cases vs “future only” cases (or unknown cases).

I understand that the feature you propose would allow you to make such a distinction, but again, what is your use case for doing so?

Breaking out early by checking unknown cases first. I admit this is not deal breaker, just a different style I’d like to see supported in the future.

···

On Jan 4, 2018, at 4:37 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 19:29 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 3:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 18:39 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 2:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 17:15 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com> wrote:

This depends on generalized `unknown case` patterns which is out of scope. I am happy to talk more about this on a different thread when this proposal gets approved.

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As has already been said, “case unknown” is source-breaking because it conflicts with any real cases named “unknown”; “\unknown” looks like a key path but isn’t, and I wonder if it would potentially conflict with existing key paths.

In any case, my point was not to bikeshed the “unknown” part, but to ask whether any consideration had been made to have the feature presented as a flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default", can't be mixed with "default", and can't be used in case patterns, why not "default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md\]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

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

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

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

Can case _ be mixed with unknown case? How can we match all compile
time known cases but exclude future cases?

What’s your use case for that? That eliminates the possibility of
“unknown case” giving you compile-time warnings for subsequently added
cases, which was the entire purpose of adding the syntax in the first place.

I was thinking of a generalized `unknown case` pattern but that is out
of scope for this proposal.
<SE-0192 Non-exhaustive enums: add `unknown` and rename to '@frozen' by jrose-apple · Pull Request #777 · apple/swift-evolution · GitHub;

switch excuse {
case .eatenByPet :
   //…
unknown case:
   // …
case _:
   // …
}

Should there be something like `case *` that would capture all

currently known cases during compile time? case * and case _ would be the
same in exhaustive enums.

This is why I was suggesting another pattern that only captures known
cases at compile time:

switch excuse {
case .eatenByPet :
   //…
case * : // All cases captured at compile time.
   // …
unknown case:
   // …
}

Sorry, I don’t understand. However you spell it, what is your use case
for this? The stated purpose of “unknown case” is to gain compile-time
exhaustiveness testing, but this would not allow for that.

switch (excuse, notifiedTeacherBeforeDeadline) {case (.eatenByPet, true):
  // …case (.thoughtItWasDueNextWeek, true):
  // …case (unknown case, true):
  // …case (_, false):
  // …}

Im referring to the future direction section in the new PR
<https://github.com/jrose-apple/swift-evolution/blob/6061c01fb4a6d742ba7213f46979c9b82891fc14/proposals/0192-non-exhaustive-enums.md#future-directions&gt;\.
The above example if from there.

I am fine with `unknown case` being required to be at the end of the
switch for now.

I think of `unknown case` as a pattern that only matches unknown cases
no matter where on the switch it is.

This is why I do not think that `default unknown` would work well once
`unknown case` can be used a pattern.

We can start a new thread on this if you’d like.

The reason I put forward “default unknown” is precisely because the
proposed feature *cannot* be used in a pattern and therefore seems more apt
as not a case.

It can not be used in a pattern now but you could in the future if left
as `case`.

It actually makes it more natural to use in the given example above
because “default unknown” could actually be used to provide compile-time
exhaustiveness checking for such a tuple pattern, whereas without being
able to use “unknown case” in a pattern you can’t write “case (unknown
case, _)”.

The way `unknown case` enforces compile-time exhaustiveness is by only
matching unknown cases. The implementation may be more close to default by
the virtue of being forced to go at the end of the switch statement now but
that should not dictate the user experience.

We seem to agree that, by virtue of not supporting use in a pattern and
being placed at the end, the feature is a flavor of default. I’m still not
sure I understand why you believe it should not be a flavor of default
going forward.

You still haven’t answered my question, though—what’s the use case for
the feature you propose?

My use case would be distinguishing between compile time known cases vs
“future only” cases (or unknown cases).

I understand that the feature you propose would allow you to make such a
distinction, but again, what is your use case for doing so?

Breaking out early by checking unknown cases first. I admit this is not
deal breaker, just a different style I’d like to see supported in the
future.

I'm still not sure I understand. How can the machine know that it's dealing
with an unknown case without first checking if it matches any known case?

···

On Fri, Jan 5, 2018 at 00:21 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 4:37 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 19:29 Cheyo J. Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 3:50 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 18:39 Cheyo J. Jimenez <cheyo@masters3d.com> >> wrote:

On Jan 4, 2018, at 2:55 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 17:15 Cheyo J. Jimenez <cheyo@masters3d.com> >>> wrote:

On Jan 4, 2018, at 11:53 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Thu, Jan 4, 2018 at 13:46 Cheyo Jimenez <cheyo@masters3d.com> wrote:

On Jan 4, 2018, at 10:49 AM, Jordan Rose <jordan_rose@apple.com> >>>>> wrote:

This depends on generalized `unknown case` patterns which is out of scope.

I am happy to talk more about this on a different thread when this proposal
gets approved.

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

Jordan

On Jan 3, 2018, at 22:56, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As has already been said, “case unknown” is source-breaking because it
conflicts with any real cases named “unknown”; “\unknown” looks like a key
path but isn’t, and I wonder if it would potentially conflict with existing
key paths.

In any case, my point was not to bikeshed the “unknown” part, but to
ask whether any consideration had been made to have the feature presented
as a flavor of default instead of a flavor of case.

On Wed, Jan 3, 2018 at 23:57 Cheyo Jimenez <cheyo@masters3d.com> >>>>> wrote:

On Jan 3, 2018, at 6:52 PM, Xiaodi Wu via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

This is a very nice revision. One bikeshedding thought:

Since "unknown case" is presented as a special kind of "default",
can't be mixed with "default", and can't be used in case patterns, why not
"default unknown" (or "unknown default") instead of "unknown case"?

`case _ :` is already a special case of default.
I’d rather have `case unknown :`
`unknown case :` is weird because of the order of `case`.

Another alternative is `case \unknown :`
`\unknown` would also allow pattern matching.

On Wed, Jan 3, 2018 at 8:05 PM, Jordan Rose via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

On Jan 2, 2018, at 18:07, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0192-non-exhaustive-enums.md
]

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

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

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

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

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

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

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

---

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

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

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

Jordan

P.S. Enjoying the Disney references. Thanks, Nevin and Dave. :-)

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

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

Which isn’t a problem right now, AFAICT. Apps compiled under older SDKs continue to work fine (sometimes better than when compiled under the new SDK, as the older one avoids new bugs). My question is about how that compatibility is accomplished today and how and why the Obj-C and Swift cases are apparently different here.

Jon

···

On Jan 6, 2018, at 6:12 PM, Javier Soto <javier.api@gmail.com> wrote:

What doesn't happen today? The issue is not when they ship a new SDK: When rebuilding your app against it, you'll get a warning for a missing case. The problem is when running the app against a newer iOS version with a newer version of the SDK where the enum has a new case.

On Sat, Jan 6, 2018 at 3:10 PM Jon Shier <jon@jonshier.com> wrote:
Except it clearly doesn’t happen today when Apple ships new SDKs. Obviously there’s an alternate mechanism used in that case. I’m just curious what it is and why Swift so desperately needs an alternative.

Jon

On Jan 6, 2018, at 5:49 PM, Javier Soto <javier.api@gmail.com> wrote:

This is very much an issue in Obj-C today. If you have an NS_ENUM defined with cases A, B, and C, this switch is correct:

int foo;
swith (e) {
case A: foo = 0; break;
case B: foo = 1; break;
case C: foo = 2; break;
}

(Note the lack of a default case)

If that enum is defined in a framework and it changes after the app is compiled (like it's the case with Apple frameworks), then that code produces no warning, yet the foo variable will have a garbage value (undefined behavior, but as far as the compiler can tell at compile time your code is fine)

Adding a default clause to that switch has the downside of not getting warnings for new added cases, like has been discussed before, which is very useful.

On Fri, Jan 5, 2018 at 7:11 PM Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:
At this point I think it might be useful to outline how binary compatibility works for Objective-C on Apple platforms right now. As an app developer I’m not intimately familiar with what happens when you run an app compiled with the iOS 10 SDK on iOS 11. Are there just runtime checks to call old code paths or something else? The more this thread goes on the more confused I get about why Swift would have this issue while it doesn’t appear to be one for Obj-C. If an enum adds a case now, I don’t have to care until I recompile using the new SDK. Is the intention for Swift to be different in this regard?

Jon Shier

On Jan 5, 2018, at 6:41 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 3, 2018, at 00:54, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

Is it hard to imagine that most everyone can get what they want and keep the syntax clean and streamlined at the same time? Without any "@" signs or other compiler hints?

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

"Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions"

When an enum changes, and the change causes the code to break, the user can be presented with migration options from an automated IDE tool. In what specific way does this not solve the issue about having to upgrade your code when using someone else's code library? This very notion implies your disgruntled about doing work when things are upgraded, is that really what this fuss is all about?

A well written language interpreter and auto-tooling IDE would not need hints embedded in the code syntax itself. Migration hints from version to version should not be a part of either the past or future version of the code library.

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

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

Jordan

...

I don't expect the community to agree on language grammar, but the common sense here on how to achieve the intended goals seems to be out of wack.

If someone can present a clear logical statement as to how an automated migration tool behind the scenes in the IDE to handle all your versioning worries, does not make this whole discussion about adding more convoluted syntax additions irrelevant, I'd love to hear it.

___________________

Sincerely,
Jason

On Tue, Jan 2, 2018 at 12:36 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Tue, Jan 2, 2018 at 12:11 PM, Jason Merchant via swift-evolution <swift-evolution@swift.org> wrote:

I think this whole thing has been unnecessarily convoluted. As a result, the majority of the replies are rabbit holes.

In my opinion, the true root of the concept in question is as follows:

A list of something is desired:
1 - Pancake
2 - Waffle
3 - Juice

Developer wishes to be able to:
A) Add new things to the list of choices in the future as they come up with new ideas
B) Sometimes select one of the choices to be chosen as the normal choice if no choice is made by the user

A and B are separate desires. In some circumstances a developer may want to add a new choice and make it the normal choice when there was no normal choice was clarified before.

I don't think this is an accurate summary of the problem being tackled here. Rather, we are how to enable the vendor of a nonexhaustive enum to add new cases without breaking binaries compiled against previous versions. There is little here to do with what a "default" should be. Indeed, it is an explicit design decision of Swift not to support types having an implicit default value.

____________________

Part 2:

After this simple desire is clear, there should be two discussions:
A) In a text only coding language, what would we like the syntax to look like? (Without regard to past-bias. What should it really be, forget what mistaken design choices were made in Swift in the past)
B) How do we approach making this happen behind the scenes?

Bonus: Given that some of us have changed our approach to programming significantly beyond text based coding, and into more dynamic mediums of programming in other niches, and even here and there in Xcode - I would recommend considering how the IDE would show a modern version of this concept. I feel too often that Swift design syntax has a lack of awareness between the distinctions of what the IDE should do, as opposed to what the syntax of the language should be, and what should be handled behind the scenes by automated tooling.

_____________________

My opinion, in answering the above questions is in preference to a simple easy to read and write syntax, something like the following:

choices Breakfast {
    Pancake, Waffle, Juice
}

If a "default" choice is desired, it is obvious to me that I would select the choice from the IDE, and it would be visually indicated that it was the default.

When changes occur, whether new choices are added, old ones are removed or changed, or a default is added, changed, or removed - a behind the scenes automated tool analyzes the changes and presents migration options through the IDE.

_____________________

Sincerely,
Jason

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

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

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

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

--
Javier Soto

--
Javier Soto