Enums and Source Compatibility

I also realize that this may be an impractical approach to solving this problem, because it would require teaching the analyzer about built products, with knowledge of what’s going to be copied, etc etc.

So, the alternative view to this would be:

- any enum that you are compiling is exhaustive
- everything else is non-exhaustive

Dave

···

On Sep 28, 2017, at 4:18 PM, Dave DeLong via swift-evolution <swift-evolution@swift.org> wrote:

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

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

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

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

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

Dave

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

On Sep 20, 2017, at 16:15, Dave DeLong <swift@davedelong.com <mailto:swift@davedelong.com>> wrote:

Hi Jordan,

One thing I’m still not clear on exhaustive vs non-exhaustive…

What will stop a developer from releasing an exhaustive enum in version 1 of their library, and then adding a new case in version 2?

With ABI stability and libraries getting updated independently of apps, this can be a major problem.

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

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

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

Jordan

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

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

···

Sent from my iPhone

On 28 Sep 2017, at 23:57, Jon Shier via swift-evolution <swift-evolution@swift.org> wrote:

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

Jon

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

On Sep 27, 2017, at 16:48, Jessie Serrino <jessie@serrino.co> wrote:

Hi there,

Just want to circle back on a few things.

You mentioned that library vendors communicate deprecation notices, but this is very prone to error. If someone misses the notice that a library puts out to communicate that the contracts of the enum have changed, this could break existing functionality they have already built.

You also mention that it's really hard for a library developer to predict how their enum will change, but it's even harder for a library developer to fully predict how a different user will use their library. As a developer, I don't want to have to look at the release notes to find out when a library has changed its contracts-- I want it to alert me in the most aggressive way possible to make sure that major changes to the library have been captured in my own code.

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

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

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

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

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

You're suggesting that we use enums when something is known to be exhaustive, and then something like this when it's not?

struct SomeOptionSetOrWhatever : Equatable, RawRepresentable {
  public static func == (lhs: SomeOptionSetOrWhatever, rhs: SomeOptionSetOrWhatever) -> Bool { return lhs.value == rhs.value }
  public static let optionOne = SomeOptionSetOrWhatever(privateInit: 1)
  public static let optionTwo = SomeOptionSetOrWhatever(privateInit: 2)
  private let value: Int
  private init(privateInit value: Int) { self.value = value }
  public init?(rawValue: Int) {
    switch rawValue {
    case 1: self = .optionOne
    case 2: self = .optionTwo
    case _: value = 0; return nil
    }
  }
  public var rawValue: Int { return value }
}

func foo(x: SomeOptionSetOrWhatever) {
  switch x {
  case .optionOne: print("first option")
  case .optionTwo: print("second option")
  default: break //without this line, it's a "switch much be exhaustive" error
  }
}

- Dave Sweeris

···

On Sep 28, 2017, at 4:15 PM, Goffredo Marocchi via swift-evolution <swift-evolution@swift.org> wrote:

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

(responses to both Jon and Goffredo's most recent point inline)

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

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

···

On Sep 28, 2017, at 16:15, Goffredo Marocchi <panajev@gmail.com> wrote:

On 28 Sep 2017, at 23:57, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

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

Jordan

I also realize that this may be an impractical approach to solving this problem, because it would require teaching the analyzer about built products, with knowledge of what’s going to be copied, etc etc.

So, the alternative view to this would be:

- any enum that you are compiling is exhaustive
- everything else is non-exhaustive

Dave

I don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

- Karl

···

On 29. Sep 2017, at 00:24, Dave DeLong via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 28, 2017, at 4:18 PM, Dave DeLong via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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

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

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

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

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

Dave

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

On Sep 20, 2017, at 16:15, Dave DeLong <swift@davedelong.com <mailto:swift@davedelong.com>> wrote:

Hi Jordan,

One thing I’m still not clear on exhaustive vs non-exhaustive…

What will stop a developer from releasing an exhaustive enum in version 1 of their library, and then adding a new case in version 2?

With ABI stability and libraries getting updated independently of apps, this can be a major problem.

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

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

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

Jordan

_______________________________________________
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 don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

We do plan on exposing an -enable-resilience flag which basically does what you describe. When a library is built without -enable-resilience, all types are assumed to be fixed layout, etc. However, we don’t want language flags to change language semantics, so exhaustive/nonexhaustive still make sense even when building without resilience, I think. When you switch over a non-exhaustive enum that came from a library built without -enable-resilience, the compiler can still use the most efficient possible access pattern and assume that no new cases will be introduced, but the type checker would still require a default case to be present. The optimizer can then basically strip out the default case as dead code.

Slava

···

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

- Karl

@Slava

If my understanding is correct. If I compile my application with the x.y.z
version of a Apple System Framework (like Cocoa). And this framework is
updated, several calls from my application to the "new" framework version
x.z.a behave like in the x.y.z version for compatibility issues right? I
see this being mentioned in the Cocoa documentations sometimes.

Is possible to do this here?

I usually don't care about non-exhaustive enums. We avoid this even in
languages which do not enforce us (using external tools sometimes). But i
can understand the "need" in some projects.

···

Em sáb, 30 de set de 2017 às 21:00, Slava Pestov via swift-evolution < swift-evolution@swift.org> escreveu:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution < > swift-evolution@swift.org> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should
be optimally compiled and used depends on what you plan to do with it. If
it’s going to be installed somewhere private and you can guarantee clients
will always have the latest version, you can assume all data types are
final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An
example of that would be a library embedded inside an iOS app bundle. If
it’s going to be installed somewhere public and expose some API (like
Apple’s frameworks), then you’re going to have to think about binary
compatibility.

That also means that in-app libraries are optimised as much as they can
be, and that resilience-related changes on the declaration side can be
limited to the limited set of Swift developers who truly have to care about
that.

We do plan on exposing an -enable-resilience flag which basically does
what you describe. When a library is built without -enable-resilience, all
types are assumed to be fixed layout, etc. However, we don’t want language
flags to change language semantics, so exhaustive/nonexhaustive still make
sense even when building without resilience, I think. When you switch over
a non-exhaustive enum that came from a library built without
-enable-resilience, the compiler can still use the most efficient possible
access pattern and assume that no new cases will be introduced, but the
type checker would still require a default case to be present. The
optimizer can then basically strip out the default case as dead code.

Slava

- Karl

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

Sorry to bother, but I still can't understand how the proposed change *without* a 'future' case in switch will change our life and what would be our steps to support our code and to not make our code buggy.
If I misunderstand something - sorry, please point me on this and I hope this also help some one like me to understand the subject better.

For example. I use OAuth2 framework, built by Carthage. Did add the OAuth2.framework to my project.

Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in my code where I switch on each case of such error instance to do my best with error: generate detailed description for user, other additional steps depending on error.

Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
Will new cases be added to that enum in future: Most likely Yes.
Do I need to switch on each case in my code? Yes.
Can I currently rely on compiler to keep my error processing in sync with error cases defined in framework? Yes.
Can new cases appear in *run-time* of my app: NO, framework in embedded.
Will I be able to rely on compiler after the proposed change? No?!
What should I do to keep my switch in sync with OAuth2Error cases after each update of OAuth2 library(framework)? Manually check if new cases are added?! Configure lint/other tools to help me with this?!

What I, as a developer, as a consumer of framework, need - is a way to exhaustively switch on *some* external non-exhaustive enums *at the moment of compilation*. And we can accomplish this only(AFAICT) with 'future' case in 'switch'.
In case we'll have 'future' case my life will not be *worse* for this project : I'll add it to my switch and still can receive help from compiler to keep switch exhaustive.

I don't support the opinion that we can't introduce 'future' case because of we can't test it:

1. Not being able to keep my switch exhaustive when I need this, and so not being able to provide users of my app with best experience - IMO is worse.
2. In my particular example, 'future' case will be *never* called, if I understand correctly.
3. If switch on non-exhaustive enum is exhaustive by fact, we can't test the 'default' branch also. So, 'future' is in same position here with 'default'
4. I believe if we'll decide we need 'future' case - we can suggest a way to call code in that case during the test process.

Seems like for embedded frameworks we should apply the same rules(regarding enums) as for sources, as we compile the app with concrete binary of framework and there just can't be new cases in enums. No?

Thank you for your time.
Vladimir.

···

On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

We do plan on exposing an -enable-resilience flag which basically does what you describe. When a library is built without -enable-resilience, all types are assumed to be fixed layout, etc. However, we don’t want language flags to change language semantics, so exhaustive/nonexhaustive still make sense even when building without resilience, I think. When you switch over a non-exhaustive enum that came from a library built without -enable-resilience, the compiler can still use the most efficient possible access pattern and assume that no new cases will be introduced, but the type checker would still require a default case to be present. The optimizer can then basically strip out the default case as dead code.

Slava

- Karl

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

@Slava

If my understanding is correct. If I compile my application with the x.y.z version of a Apple System Framework (like Cocoa). And this framework is updated, several calls from my application to the "new" framework version x.z.a behave like in the x.y.z version for compatibility issues right? I see this being mentioned in the Cocoa documentations sometimes.

Is possible to do this here?

Unfortunately, no. That behavior is manually implemented by the framework authors.

Dave

···

On Oct 2, 2017, at 11:29 AM, Wallacy via swift-evolution <swift-evolution@swift.org> wrote:

I usually don't care about non-exhaustive enums. We avoid this even in languages which do not enforce us (using external tools sometimes). But i can understand the "need" in some projects.

Em sáb, 30 de set de 2017 às 21:00, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> escreveu:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

We do plan on exposing an -enable-resilience flag which basically does what you describe. When a library is built without -enable-resilience, all types are assumed to be fixed layout, etc. However, we don’t want language flags to change language semantics, so exhaustive/nonexhaustive still make sense even when building without resilience, I think. When you switch over a non-exhaustive enum that came from a library built without -enable-resilience, the compiler can still use the most efficient possible access pattern and assume that no new cases will be introduced, but the type checker would still require a default case to be present. The optimizer can then basically strip out the default case as dead code.

Slava

- Karl

_______________________________________________
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 exists, but it's imperfect: it's possible that an app and its embedded frameworks are not all linked against the same version of the SDK, and then the system framework has to go with whatever the app does. (It's more important to behave consistently within a process.)

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

Jordan

···

On Oct 2, 2017, at 10:29, Wallacy via swift-evolution <swift-evolution@swift.org> wrote:

@Slava

If my understanding is correct. If I compile my application with the x.y.z version of a Apple System Framework (like Cocoa). And this framework is updated, several calls from my application to the "new" framework version x.z.a behave like in the x.y.z version for compatibility issues right? I see this being mentioned in the Cocoa documentations sometimes.

Is possible to do this here?

I usually don't care about non-exhaustive enums. We avoid this even in languages which do not enforce us (using external tools sometimes). But i can understand the "need" in some projects.

I don't think I have anything to say on this topic that I haven't already said:

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

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

Jordan

···

On Oct 2, 2017, at 08:25, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

Sorry to bother, but I still can't understand how the proposed change *without* a 'future' case in switch will change our life and what would be our steps to support our code and to not make our code buggy.
If I misunderstand something - sorry, please point me on this and I hope this also help some one like me to understand the subject better.

For example. I use OAuth2 framework, built by Carthage. Did add the OAuth2.framework to my project.

Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in my code where I switch on each case of such error instance to do my best with error: generate detailed description for user, other additional steps depending on error.

Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
Will new cases be added to that enum in future: Most likely Yes.
Do I need to switch on each case in my code? Yes.
Can I currently rely on compiler to keep my error processing in sync with error cases defined in framework? Yes.
Can new cases appear in *run-time* of my app: NO, framework in embedded.
Will I be able to rely on compiler after the proposed change? No?!
What should I do to keep my switch in sync with OAuth2Error cases after each update of OAuth2 library(framework)? Manually check if new cases are added?! Configure lint/other tools to help me with this?!

What I, as a developer, as a consumer of framework, need - is a way to exhaustively switch on *some* external non-exhaustive enums *at the moment of compilation*. And we can accomplish this only(AFAICT) with 'future' case in 'switch'.
In case we'll have 'future' case my life will not be *worse* for this project : I'll add it to my switch and still can receive help from compiler to keep switch exhaustive.

I don't support the opinion that we can't introduce 'future' case because of we can't test it:

1. Not being able to keep my switch exhaustive when I need this, and so not being able to provide users of my app with best experience - IMO is worse.
2. In my particular example, 'future' case will be *never* called, if I understand correctly.
3. If switch on non-exhaustive enum is exhaustive by fact, we can't test the 'default' branch also. So, 'future' is in same position here with 'default'
4. I believe if we'll decide we need 'future' case - we can suggest a way to call code in that case during the test process.

Seems like for embedded frameworks we should apply the same rules(regarding enums) as for sources, as we compile the app with concrete binary of framework and there just can't be new cases in enums. No?

Thank you for your time.
Vladimir.

On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

We do plan on exposing an -enable-resilience flag which basically does what you describe. When a library is built without -enable-resilience, all types are assumed to be fixed layout, etc. However, we don’t want language flags to change language semantics, so exhaustive/nonexhaustive still make sense even when building without resilience, I think. When you switch over a non-exhaustive enum that came from a library built without -enable-resilience, the compiler can still use the most efficient possible access pattern and assume that no new cases will be introduced, but the type checker would still require a default case to be present. The optimizer can then basically strip out the default case as dead code.
Slava

- Karl

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

+1 for everything Vladimir says - which is why I'm pushing for switch! (just like try!, etc.) which would preserve current behavior.

···

On Oct 2, 2017, at 5:25 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

Sorry to bother, but I still can't understand how the proposed change *without* a 'future' case in switch will change our life and what would be our steps to support our code and to not make our code buggy.
If I misunderstand something - sorry, please point me on this and I hope this also help some one like me to understand the subject better.

For example. I use OAuth2 framework, built by Carthage. Did add the OAuth2.framework to my project.

Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in my code where I switch on each case of such error instance to do my best with error: generate detailed description for user, other additional steps depending on error.

Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
Will new cases be added to that enum in future: Most likely Yes.
Do I need to switch on each case in my code? Yes.
Can I currently rely on compiler to keep my error processing in sync with error cases defined in framework? Yes.
Can new cases appear in *run-time* of my app: NO, framework in embedded.
Will I be able to rely on compiler after the proposed change? No?!
What should I do to keep my switch in sync with OAuth2Error cases after each update of OAuth2 library(framework)? Manually check if new cases are added?! Configure lint/other tools to help me with this?!

What I, as a developer, as a consumer of framework, need - is a way to exhaustively switch on *some* external non-exhaustive enums *at the moment of compilation*. And we can accomplish this only(AFAICT) with 'future' case in 'switch'.
In case we'll have 'future' case my life will not be *worse* for this project : I'll add it to my switch and still can receive help from compiler to keep switch exhaustive.

I don't support the opinion that we can't introduce 'future' case because of we can't test it:

1. Not being able to keep my switch exhaustive when I need this, and so not being able to provide users of my app with best experience - IMO is worse.
2. In my particular example, 'future' case will be *never* called, if I understand correctly.
3. If switch on non-exhaustive enum is exhaustive by fact, we can't test the 'default' branch also. So, 'future' is in same position here with 'default'
4. I believe if we'll decide we need 'future' case - we can suggest a way to call code in that case during the test process.

Seems like for embedded frameworks we should apply the same rules(regarding enums) as for sources, as we compile the app with concrete binary of framework and there just can't be new cases in enums. No?

Thank you for your time.
Vladimir.

On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

We do plan on exposing an -enable-resilience flag which basically does what you describe. When a library is built without -enable-resilience, all types are assumed to be fixed layout, etc. However, we don’t want language flags to change language semantics, so exhaustive/nonexhaustive still make sense even when building without resilience, I think. When you switch over a non-exhaustive enum that came from a library built without -enable-resilience, the compiler can still use the most efficient possible access pattern and assume that no new cases will be introduced, but the type checker would still require a default case to be present. The optimizer can then basically strip out the default case as dead code.
Slava

- Karl

_______________________________________________
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 don't think I have anything to say on this topic that I haven't already said:

- Switching exhaustively over non-exhaustive enums is uncommon.

[Citation Needed]
As I pointed out in my email in the review thread, there are a significant number of enums in Apple's own frameworks that are commonly switched over exhaustively.

Additionally, uncommon or not, when this does come up, it's an enormous issue. Big enough that "it's uncommon" isn't sufficient justification for ignoring it.

- It's more important for a library to build without errors when its dependencies change than it is to get an error. (This doesn't apply to warnings, though.)

Not sure I agree with this. In fact I think if anything it's more important for libraries than clients. Especially in the case of a new enum value it should support but doesn't.

- Untestable code is dangerous, so having a language feature inherently for untestable code seems bad.

A `default` case in the same situation is just as untestable, as you even point out in the proposal. Why do you keep using this as an argument against `future` but not `default`?

···

On Oct 2, 2017, at 2:10 PM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

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

Jordan

On Oct 2, 2017, at 08:25, Vladimir.S via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sorry to bother, but I still can't understand how the proposed change *without* a 'future' case in switch will change our life and what would be our steps to support our code and to not make our code buggy.
If I misunderstand something - sorry, please point me on this and I hope this also help some one like me to understand the subject better.

For example. I use OAuth2 framework, built by Carthage. Did add the OAuth2.framework to my project.

Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in my code where I switch on each case of such error instance to do my best with error: generate detailed description for user, other additional steps depending on error.

Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
Will new cases be added to that enum in future: Most likely Yes.
Do I need to switch on each case in my code? Yes.
Can I currently rely on compiler to keep my error processing in sync with error cases defined in framework? Yes.
Can new cases appear in *run-time* of my app: NO, framework in embedded.
Will I be able to rely on compiler after the proposed change? No?!
What should I do to keep my switch in sync with OAuth2Error cases after each update of OAuth2 library(framework)? Manually check if new cases are added?! Configure lint/other tools to help me with this?!

What I, as a developer, as a consumer of framework, need - is a way to exhaustively switch on *some* external non-exhaustive enums *at the moment of compilation*. And we can accomplish this only(AFAICT) with 'future' case in 'switch'.
In case we'll have 'future' case my life will not be *worse* for this project : I'll add it to my switch and still can receive help from compiler to keep switch exhaustive.

I don't support the opinion that we can't introduce 'future' case because of we can't test it:

1. Not being able to keep my switch exhaustive when I need this, and so not being able to provide users of my app with best experience - IMO is worse.
2. In my particular example, 'future' case will be *never* called, if I understand correctly.
3. If switch on non-exhaustive enum is exhaustive by fact, we can't test the 'default' branch also. So, 'future' is in same position here with 'default'
4. I believe if we'll decide we need 'future' case - we can suggest a way to call code in that case during the test process.

Seems like for embedded frameworks we should apply the same rules(regarding enums) as for sources, as we compile the app with concrete binary of framework and there just can't be new cases in enums. No?

Thank you for your time.
Vladimir.

On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

We do plan on exposing an -enable-resilience flag which basically does what you describe. When a library is built without -enable-resilience, all types are assumed to be fixed layout, etc. However, we don’t want language flags to change language semantics, so exhaustive/nonexhaustive still make sense even when building without resilience, I think. When you switch over a non-exhaustive enum that came from a library built without -enable-resilience, the compiler can still use the most efficient possible access pattern and assume that no new cases will be introduced, but the type checker would still require a default case to be present. The optimizer can then basically strip out the default case as dead code.
Slava

- Karl

_______________________________________________
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

Can you go into more detail about why the core team didn't like this?

public enum HomeworkExcuse {
  case eatenByPet
  case thoughtItWasDueNextWeek
  default // NEW}

To me this is very close to an ideal solution, it fixes ABI concerns, it
has sensible defaults. If it was changed a little bit:

public enum HomeworkExcuse {
  case eatenByPet
  case thoughtItWasDueNextWeek
  fallback unknown // NEW}

Then I believe you would be able to have an exhaustive switch like this:

switch thing {
  case eatenByPet: break
  case thoughtItWasDueNextWeek: break
  case unknown: break}

Which would *still allow compile-time errors if new cases are introduced*,
while providing a concise way to show something is not exhaustible.

This would also *support existing enums with "unknown" equivalent cases*
would be able to explicitly label those fields as fallback without needing
to make large code changes.

I see no reason why you shouldn't be able to use ".unknown", which *should
still allow this to be testable*.

Thanks,
Andrew

···

On Tue, Oct 3, 2017 at 8:10 AM, Jordan Rose via swift-evolution < swift-evolution@swift.org> wrote:

I don't think I have anything to say on this topic that I haven't already
said:

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

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

Jordan

On Oct 2, 2017, at 08:25, Vladimir.S via swift-evolution < > swift-evolution@swift.org> wrote:

Sorry to bother, but I still can't understand how the proposed change
*without* a 'future' case in switch will change our life and what would be
our steps to support our code and to not make our code buggy.
If I misunderstand something - sorry, please point me on this and I hope
this also help some one like me to understand the subject better.

For example. I use OAuth2 framework, built by Carthage. Did add the
OAuth2.framework to my project.

Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in
my code where I switch on each case of such error instance to do my best
with error: generate detailed description for user, other additional steps
depending on error.

Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
Will new cases be added to that enum in future: Most likely Yes.
Do I need to switch on each case in my code? Yes.
Can I currently rely on compiler to keep my error processing in sync with
error cases defined in framework? Yes.
Can new cases appear in *run-time* of my app: NO, framework in embedded.
Will I be able to rely on compiler after the proposed change? No?!
What should I do to keep my switch in sync with OAuth2Error cases after
each update of OAuth2 library(framework)? Manually check if new cases are
added?! Configure lint/other tools to help me with this?!

What I, as a developer, as a consumer of framework, need - is a way to
exhaustively switch on *some* external non-exhaustive enums *at the moment
of compilation*. And we can accomplish this only(AFAICT) with 'future' case
in 'switch'.
In case we'll have 'future' case my life will not be *worse* for this
project : I'll add it to my switch and still can receive help from compiler
to keep switch exhaustive.

I don't support the opinion that we can't introduce 'future' case because
of we can't test it:

1. Not being able to keep my switch exhaustive when I need this, and so
not being able to provide users of my app with best experience - IMO is
worse.
2. In my particular example, 'future' case will be *never* called, if I
understand correctly.
3. If switch on non-exhaustive enum is exhaustive by fact, we can't test
the 'default' branch also. So, 'future' is in same position here with
'default'
4. I believe if we'll decide we need 'future' case - we can suggest a way
to call code in that case during the test process.

Seems like for embedded frameworks we should apply the same
rules(regarding enums) as for sources, as we compile the app with concrete
binary of framework and there just can't be new cases in enums. No?

Thank you for your time.
Vladimir.

On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution < > swift-evolution@swift.org <mailto:swift-evolution@swift.org > <swift-evolution@swift.org>>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should
be optimally compiled and used depends on what you plan to do with it. If
it’s going to be installed somewhere private and you can guarantee clients
will always have the latest version, you can assume all data types are
final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An
example of that would be a library embedded inside an iOS app bundle. If
it’s going to be installed somewhere public and expose some API (like
Apple’s frameworks), then you’re going to have to think about binary
compatibility.

That also means that in-app libraries are optimised as much as they can
be, and that resilience-related changes on the declaration side can be
limited to the limited set of Swift developers who truly have to care about
that.

We do plan on exposing an -enable-resilience flag which basically does
what you describe. When a library is built without -enable-resilience, all
types are assumed to be fixed layout, etc. However, we don’t want language
flags to change language semantics, so exhaustive/nonexhaustive still make
sense even when building without resilience, I think. When you switch over
a non-exhaustive enum that came from a library built without
-enable-resilience, the compiler can still use the most efficient possible
access pattern and assume that no new cases will be introduced, but the
type checker would still require a default case to be present. The
optimizer can then basically strip out the default case as dead code.
Slava

- Karl

_______________________________________________
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

Can you go into more detail about why the core team didn't like this?
public enum HomeworkExcuse {
  case eatenByPet
  case thoughtItWasDueNextWeek
  default // NEW
}

To me this is very close to an ideal solution, it fixes ABI concerns, it has sensible defaults. If it was changed a little bit:

public enum HomeworkExcuse {
  case eatenByPet
  case thoughtItWasDueNextWeek
  fallback unknown // NEW
}

Then I believe you would be able to have an exhaustive switch like this:

switch thing {
  case eatenByPet: break
  case thoughtItWasDueNextWeek: break
  case unknown: break
}

Which would still allow compile-time errors if new cases are introduced, while providing a concise way to show something is not exhaustible.

This would also support existing enums with "unknown" equivalent cases would be able to explicitly label those fields as fallback without needing to make large code changes.

I see no reason why you shouldn't be able to use ".unknown", which should still allow this to be testable.

This is an extremely elegant solution which seems to solve all problems very nicely!!
Yes, instead of marking the enum as non-exhaustive let’s just add a placeholder case for future cases.
I’m very much in favor of this.

-Thorsten

···

Am 21.12.2017 um 23:48 schrieb Andrew Bennett via swift-evolution <swift-evolution@swift.org>:

Thanks,
Andrew

On Tue, Oct 3, 2017 at 8:10 AM, Jordan Rose via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
I don't think I have anything to say on this topic that I haven't already said:

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

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

Jordan

On Oct 2, 2017, at 08:25, Vladimir.S via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sorry to bother, but I still can't understand how the proposed change *without* a 'future' case in switch will change our life and what would be our steps to support our code and to not make our code buggy.
If I misunderstand something - sorry, please point me on this and I hope this also help some one like me to understand the subject better.

For example. I use OAuth2 framework, built by Carthage. Did add the OAuth2.framework to my project.

Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in my code where I switch on each case of such error instance to do my best with error: generate detailed description for user, other additional steps depending on error.

Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
Will new cases be added to that enum in future: Most likely Yes.
Do I need to switch on each case in my code? Yes.
Can I currently rely on compiler to keep my error processing in sync with error cases defined in framework? Yes.
Can new cases appear in *run-time* of my app: NO, framework in embedded.
Will I be able to rely on compiler after the proposed change? No?!
What should I do to keep my switch in sync with OAuth2Error cases after each update of OAuth2 library(framework)? Manually check if new cases are added?! Configure lint/other tools to help me with this?!

What I, as a developer, as a consumer of framework, need - is a way to exhaustively switch on *some* external non-exhaustive enums *at the moment of compilation*. And we can accomplish this only(AFAICT) with 'future' case in 'switch'.
In case we'll have 'future' case my life will not be *worse* for this project : I'll add it to my switch and still can receive help from compiler to keep switch exhaustive.

I don't support the opinion that we can't introduce 'future' case because of we can't test it:

1. Not being able to keep my switch exhaustive when I need this, and so not being able to provide users of my app with best experience - IMO is worse.
2. In my particular example, 'future' case will be *never* called, if I understand correctly.
3. If switch on non-exhaustive enum is exhaustive by fact, we can't test the 'default' branch also. So, 'future' is in same position here with 'default'
4. I believe if we'll decide we need 'future' case - we can suggest a way to call code in that case during the test process.

Seems like for embedded frameworks we should apply the same rules(regarding enums) as for sources, as we compile the app with concrete binary of framework and there just can't be new cases in enums. No?

Thank you for your time.
Vladimir.

On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org> <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library should be optimally compiled and used depends on what you plan to do with it. If it’s going to be installed somewhere private and you can guarantee clients will always have the latest version, you can assume all data types are final, @_fixed_layout, @exhaustive, whatever (essentially in-module). An example of that would be a library embedded inside an iOS app bundle. If it’s going to be installed somewhere public and expose some API (like Apple’s frameworks), then you’re going to have to think about binary compatibility.

That also means that in-app libraries are optimised as much as they can be, and that resilience-related changes on the declaration side can be limited to the limited set of Swift developers who truly have to care about that.

We do plan on exposing an -enable-resilience flag which basically does what you describe. When a library is built without -enable-resilience, all types are assumed to be fixed layout, etc. However, we don’t want language flags to change language semantics, so exhaustive/nonexhaustive still make sense even when building without resilience, I think. When you switch over a non-exhaustive enum that came from a library built without -enable-resilience, the compiler can still use the most efficient possible access pattern and assume that no new cases will be introduced, but the type checker would still require a default case to be present. The optimizer can then basically strip out the default case as dead code.
Slava

- Karl

_______________________________________________
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

Actually this make much more sense than original proposal.

Good call!

Just to check...

Its not better this?

public enum HomeworkExcuse {
  case eatenByPet
  default case thoughtItWasDueNextWeek}

If i understood correctly, if the enum is not exhaustible, must be
considered as this default value after a new case be introduced in a new
version of the lib right?

And for this one:

public enum HomeworkExcuse {
  case eatenByPet
  case thoughtItWasDueNextWeek
  fallback unknown // any word of course right?}

In this case fallback is the same as default right? But has as explicit new
label/case?

So, this is not enough?

public enum HomeworkExcuse {
  case eatenByPet
  fallback case thoughtItWasDueNextWeek}

If the dev wants to do make a default/fallback just use them for a regular
case, if not, if he wants to make a totally different case for a fallback
just declare another one to be the default/fallback (like unknown).

I understood your idea correctly?

···

Em qui, 21 de dez de 2017 às 20:48, Andrew Bennett via swift-evolution < swift-evolution@swift.org> escreveu:

Can you go into more detail about why the core team didn't like this?

public enum HomeworkExcuse {
  case eatenByPet
  case thoughtItWasDueNextWeek
  default // NEW}

To me this is very close to an ideal solution, it fixes ABI concerns, it
has sensible defaults. If it was changed a little bit:

public enum HomeworkExcuse {
  case eatenByPet
  case thoughtItWasDueNextWeek
  fallback unknown // NEW}

Then I believe you would be able to have an exhaustive switch like this:

switch thing {
  case eatenByPet: break
  case thoughtItWasDueNextWeek: break
  case unknown: break}

Which would *still allow compile-time errors if new cases are introduced*,
while providing a concise way to show something is not exhaustible.

This would also *support existing enums with "unknown" equivalent cases*
would be able to explicitly label those fields as fallback without needing
to make large code changes.

I see no reason why you shouldn't be able to use ".unknown", which *should
still allow this to be testable*.

Thanks,
Andrew

On Tue, Oct 3, 2017 at 8:10 AM, Jordan Rose via swift-evolution < > swift-evolution@swift.org> wrote:

I don't think I have anything to say on this topic that I haven't already
said:

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

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

Jordan

On Oct 2, 2017, at 08:25, Vladimir.S via swift-evolution < >> swift-evolution@swift.org> wrote:

Sorry to bother, but I still can't understand how the proposed change
*without* a 'future' case in switch will change our life and what would be
our steps to support our code and to not make our code buggy.
If I misunderstand something - sorry, please point me on this and I hope
this also help some one like me to understand the subject better.

For example. I use OAuth2 framework, built by Carthage. Did add the
OAuth2.framework to my project.

Currently OAuth2 exports 'public enum OAuth2Error'. I do have a place in
my code where I switch on each case of such error instance to do my best
with error: generate detailed description for user, other additional steps
depending on error.

Will/should author of OAuth2 make OAuth2Error 'exhaustive' ? No.
Will new cases be added to that enum in future: Most likely Yes.
Do I need to switch on each case in my code? Yes.
Can I currently rely on compiler to keep my error processing in sync with
error cases defined in framework? Yes.
Can new cases appear in *run-time* of my app: NO, framework in embedded.
Will I be able to rely on compiler after the proposed change? No?!
What should I do to keep my switch in sync with OAuth2Error cases after
each update of OAuth2 library(framework)? Manually check if new cases are
added?! Configure lint/other tools to help me with this?!

What I, as a developer, as a consumer of framework, need - is a way to
exhaustively switch on *some* external non-exhaustive enums *at the moment
of compilation*. And we can accomplish this only(AFAICT) with 'future' case
in 'switch'.
In case we'll have 'future' case my life will not be *worse* for this
project : I'll add it to my switch and still can receive help from compiler
to keep switch exhaustive.

I don't support the opinion that we can't introduce 'future' case because
of we can't test it:

1. Not being able to keep my switch exhaustive when I need this, and so
not being able to provide users of my app with best experience - IMO is
worse.
2. In my particular example, 'future' case will be *never* called, if I
understand correctly.
3. If switch on non-exhaustive enum is exhaustive by fact, we can't test
the 'default' branch also. So, 'future' is in same position here with
'default'
4. I believe if we'll decide we need 'future' case - we can suggest a way
to call code in that case during the test process.

Seems like for embedded frameworks we should apply the same
rules(regarding enums) as for sources, as we compile the app with concrete
binary of framework and there just can't be new cases in enums. No?

Thank you for your time.
Vladimir.

On 01.10.2017 3:00, Slava Pestov via swift-evolution wrote:

On Sep 30, 2017, at 4:46 PM, Karl Wagner via swift-evolution < >> swift-evolution@swift.org <mailto:swift-evolution@swift.org >> <swift-evolution@swift.org>>> wrote:

I don’t see how it’s impractical. Quite a lot about how the library
should be optimally compiled and used depends on what you plan to do with
it. If it’s going to be installed somewhere private and you can guarantee
clients will always have the latest version, you can assume all data types
are final, @_fixed_layout, @exhaustive, whatever (essentially in-module).
An example of that would be a library embedded inside an iOS app bundle. If
it’s going to be installed somewhere public and expose some API (like
Apple’s frameworks), then you’re going to have to think about binary
compatibility.

That also means that in-app libraries are optimised as much as they can
be, and that resilience-related changes on the declaration side can be
limited to the limited set of Swift developers who truly have to care about
that.

We do plan on exposing an -enable-resilience flag which basically does
what you describe. When a library is built without -enable-resilience, all
types are assumed to be fixed layout, etc. However, we don’t want language
flags to change language semantics, so exhaustive/nonexhaustive still make
sense even when building without resilience, I think. When you switch over
a non-exhaustive enum that came from a library built without
-enable-resilience, the compiler can still use the most efficient possible
access pattern and assume that no new cases will be introduced, but the
type checker would still require a default case to be present. The
optimizer can then basically strip out the default case as dead code.
Slava

- Karl

_______________________________________________
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

I prefer the model where the developer writing the switch controls the desired behaviour. There are many different usage scenarios here, and it seems to me the only people that can really make the right call are the ones writing the switches, and the ones building and linking their higher level modules. Yes, the library authors can declare if the enum will never change, and that is useful and can be used to determine certain compile-time behaviours. And the special rules for code in the same module as the enum are also good. But however useful, these cover only a minority of cases.

If I know I’m building HomeworkExcuses.framework from source, I don’t want to be forced to write default or unknown clauses in already-exhaustive (at the time of compilation) switches because I end up with code that (very often) has never been executed or tested and that will silently activate when a new enum is added later (i.e. it’s a logic-bomb). I kinda want that to be a source-breaking change in some situations. I want to be forced to review my code, my error handling, etc. But of course this isn’t true for everyone and in every situation. Even for the same enum. Maybe I want that behaviour in one module I’m writing, but in another module that has its own clients but uses that same enum, I may want to offer some better source stability and therefore write my switches differently (i.e. with a default/unknown case).

This leads me toward the future style that has been mentioned earlier in this thread and by the “Vladimir.S”'s post linked from the main proposal.

edit: and of course I haven’t said anything about binary compatibility or non-built-from-source frameworks which adds another dimension, but since this topic says “Source Compatibility” that’s all I’m offering my opinion on :)