> On Sep 6, 2017, at 5:44 AM, Rod Brown via swift-evolution > <swift-evolution@swift.org> wrote: > >> >> On 6 Sep 2017, at 12:05 pm, Jarod Long via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >> From the perspective of primarily an app developer rather than library >> author, I'm not a big fan of this change. I find myself in the "unhappy with >> the loss of compiler warnings" camp -- if I'm switching over every case of >> an enum, then I almost certainly want to be notified that a new case has >> been added by way of a compiler error than via the arbitrary runtime >> behavior I added in the previously-unreachable default case. > > I think what you’re really asking for here is the “future” case mentioned in > the Alternatives Considered section. I think that Jordan makes a good point > that this would result in untestable code, which is bad practice. While the > lack of clear highlighting of non-exhaustive cases is undesirable, I think > untestable code is a much larger problem here.
This is generally the switch! that I've suggested listed in alternatives as well - that generally brings current behavior. For a project that is regularly maintained, I believe that this makes sense given that the enums are only likely to change once a year at most (with new OS releases)... > > Either way we need a way to handle forward compatibility for our code when > cases get added to external frameworks, that much is clear. Swift is broken > in regards to this, and we need to handle it somehow. I’m hoping you’re not > suggesting that we just don’t make this change at all. We need this for > forward compatibility for framework development with Swift. > >> >> This seems like a clear situation where source compatibility is not desired >> to me. For those who want to maximize compatibility, it is possible to opt >> into it by adding a default case to an exhaustive switch over a library >> enum, but the reverse is not true if this change is made as-is. You can't >> opt into an exhaustive switch for nonexhaustive enums if handling every case >> is valued over source compatibility. >> >> A secondary concern I have is that this introduces extra complexity that >> could be confusing for new Swift developers. The current enum exhaustivity >> rules are consistent and easy to explain, but they become more cumbersome >> with this added exception that only applies to some enums that specifically >> only come from outside the current module. If this change is made, I would >> encourage some effort towards a specific error message when switching over >> all cases of a nonexhaustive enum without a default case. Rather than the >> existing "Switch must be exhaustive", I think it would go a long way towards >> avoiding confusion to say something like "Switch over a nonexhaustive enum >> must have a default case". >> >> In any case, I don't think these are terrible issues -- I agree with the >> proposal's statement that switches over nonexhaustive enums are generally >> uncommon. But if that's true, it feels like the source compatibility >> motivation is weak, since not much code is affected anyways. Perhaps the >> benefits from a library author's perspective make this change worth it, but >> at least for me and my coworkers, it would be an unwelcome change overall. >> >> Jarod >> >> On Sep 5, 2017, 17:19 -0700, Jordan Rose via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>, wrote: >>> I've taken everyone's feedback into consideration and written this up as a >>> proposal: >>> https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/nnnn-non-exhaustive-enums.md >>> >>> <https://github.com/jrose-apple/swift-evolution/blob/non-exhaustive-enums/proposals/nnnn-non-exhaustive-enums.md>. >>> The next step is working on an implementation, but if people have further >>> pre-review comments I'd be happy to hear them. >>> >>> Jordan >>> >>> >>>> On Aug 8, 2017, at 15:27, Jordan Rose <jordan_r...@apple.com >>>> <mailto:jordan_r...@apple.com>> wrote: >>>> >>>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle back to >>>> an issue that's been around for a while: the source compatibility of >>>> enums. Today, it's an error to switch over an enum without handling all >>>> the cases, but this breaks down in a number of ways: >>>> >>>> - A C enum may have "private cases" that aren't defined inside the >>>> original enum declaration, and there's no way to detect these in a switch >>>> without dropping down to the rawValue. >>>> - For the same reason, the compiler-synthesized 'init(rawValue:)' on an >>>> imported enum never produces 'nil', because who knows how anyone's using C >>>> enums anyway? >>>> - Adding a new case to a Swift enum in a library breaks any client code >>>> that was trying to switch over it. >>>> >>>> (This list might sound familiar, and that's because it's from a message of >>>> mine on a thread started by Matthew Johnson back in February called >>>> "[Pitch] consistent public access modifiers". Most of the rest of this >>>> email is going to go the same way, because we still need to make progress >>>> here.) >>>> >>>> At the same time, we really like our exhaustive switches, especially over >>>> enums we define ourselves. And there's a performance side to this whole >>>> thing too; if all cases of an enum are known, it can be passed around much >>>> more efficiently than if it might suddenly grow a new case containing a >>>> struct with 5000 Strings in it. >>>> >>>> >>>> Behavior >>>> >>>> I think there's certain behavior that is probably not terribly >>>> controversial: >>>> >>>> - When enums are imported from Apple frameworks, they should always >>>> require a default case, except for a few exceptions like NSRectEdge. (It's >>>> Apple's job to handle this and get it right, but if we get it wrong with >>>> an imported enum there's still the workaround of dropping down to the raw >>>> value.) >>>> - When I define Swift enums in the current framework, there's obviously no >>>> compatibility issues; we should allow exhaustive switches. >>>> >>>> Everything else falls somewhere in the middle, both for enums defined in >>>> Objective-C: >>>> >>>> - If I define an Objective-C enum in the current framework, should it >>>> allow exhaustive switching, because there are no compatibility issues, or >>>> not, because there could still be private cases defined in a .m file? >>>> - If there's an Objective-C enum in another framework (that I built >>>> locally with Xcode, Carthage, CocoaPods, SwiftPM, etc.), should it allow >>>> exhaustive switching, because there are no binary compatibility issues, or >>>> not, because there may be source compatibility issues? We'd really like >>>> adding a new enum case to not be a breaking change even at the source >>>> level. >>>> - If there's an Objective-C enum coming in through a bridging header, >>>> should it allow exhaustive switching, because I might have defined it >>>> myself, or not, because it might be non-modular content I've used the >>>> bridging header to import? >>>> >>>> And in Swift: >>>> >>>> - If there's a Swift enum in another framework I built locally, should it >>>> allow exhaustive switching, because there are no binary compatibility >>>> issues, or not, because there may be source compatibility issues? Again, >>>> we'd really like adding a new enum case to notbe a breaking change even at >>>> the source level. >>>> >>>> Let's now flip this to the other side of the equation. I've been talking >>>> about us disallowing exhaustive switching, i.e. "if the enum might grow >>>> new cases you must have a 'default' in a switch". In previous (in-person) >>>> discussions about this feature, it's been pointed out that the code in an >>>> otherwise-fully-covered switch is, by definition, unreachable, and >>>> therefore untestable. This also isn't a desirable situation to be in, but >>>> it's mitigated somewhat by the fact that there probably aren't many >>>> framework enums you should exhaustively switch over anyway. (Think about >>>> Apple's frameworks again.) I don't have a great answer, though. >>>> >>>> For people who like exhaustive switches, we thought about adding a new >>>> kind of 'default'—let's call it 'unknownCase' just to be able to talk >>>> about it. This lets you get warnings when you update to a new SDK, but is >>>> even more likely to be untested code. We didn't think this was worth the >>>> complexity. >>>> >>>> >>>> Terminology >>>> >>>> The "Library Evolution >>>> <http://jrose-apple.github.io/swift-library-evolution/>" doc (mostly >>>> written by me) originally called these "open" and "closed" enums >>>> ("requires a default" and "allows exhaustive switching", respectively), >>>> but this predated the use of 'open' to describe classes and class members. >>>> Matthew's original thread did suggest using 'open' for enums as well, but >>>> I argued against that, for a few reasons: >>>> >>>> - For classes, "open" and "non-open" restrict what the client can do. For >>>> enums, it's more about providing the client with additional guarantees—and >>>> "non-open" is the one with more guarantees. >>>> - The "safe" default is backwards: a merely-public class can be made >>>> 'open', while an 'open' class cannot be made non-open. Conversely, an >>>> "open" enum can be made "closed" (making default cases unnecessary), but a >>>> "closed" enum cannot be made "open". >>>> >>>> That said, Clang now has an 'enum_extensibility' attribute that does take >>>> 'open' or 'closed' as an argument. >>>> >>>> On Matthew's thread, a few other possible names came up, though mostly >>>> only for the "closed" case: >>>> >>>> - 'final': has the right meaning abstractly, but again it behaves >>>> differently than 'final' on a class, which is a restriction on code >>>> elsewhere in the same module. >>>> - 'locked': reasonable, but not a standard term, and could get confused >>>> with the concurrency concept >>>> - 'exhaustive': matches how we've been explaining it (with an "exhaustive >>>> switch"), but it's not exactly the enum that's exhaustive, and it's a long >>>> keyword to actually write in source. >>>> >>>> - 'extensible': matches the Clang attribute, but also long >>>> >>>> >>>> I don't have better names than "open" and "closed", so I'll continue using >>>> them below even though I avoided them above. But I would really like to >>>> find some. >>>> >>>> >>>> Proposal >>>> >>>> Just to have something to work off of, I propose the following: >>>> >>>> 1. All enums (NS_ENUMs) imported from Objective-C are "open" unless they >>>> are declared "non-open" in some way (likely using the enum_extensibility >>>> attribute mentioned above). >>>> 2. All public Swift enums in modules compiled "with resilience" (still to >>>> be designed) have the option to be either "open" or "closed". This only >>>> applies to libraries not distributed with an app, where binary >>>> compatibility is a concern. >>>> 3. All public Swift enums in modules compiled from source have the option >>>> to be either "open" or "closed". >>>> 4. In Swift 5 mode, a public enum should be required to declare if it is >>>> "open" or "closed", so that it's a conscious decision on the part of the >>>> library author. (I'm assuming we'll have a "Swift 4 compatibility mode" >>>> next year that would leave unannotated enums as "closed".) >>>> 5. None of this affects non-public enums. >>>> >>>> (4) is the controversial one, I expect. "Open" enums are by far the common >>>> case in Apple's frameworks, but that may be less true in Swift. >>>> >>>> >>>> Why now? >>>> >>>> Source compatibility was a big issue in Swift 4, and will continue to be >>>> an important requirement going into Swift 5. But this also has an impact >>>> on the ABI: if an enum is "closed", it can be accessed more efficiently by >>>> a client. We don't have to do this before ABI stability—we could access >>>> all enums the slow way if the library cares about binary compatibility, >>>> and add another attribute for this distinction later—but it would be nice™ >>>> (an easy model for developers to understand) if "open" vs. "closed" was >>>> also the primary distinction between "indirect access" vs. "direct access". >>>> >>>> I've written quite enough at this point. Looking forward to feedback! >>>> 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 <mailto:swift-evolution@swift.org> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution