I disagree. Closed is indeed the stronger guarantee, but APIs are
designed differently in Swift; closed is a sensible default. We
shouldn’t need to define new keywords and increase the surface area of
the language for something that has verisimilitude with the existing
open syntax.
Sincerely,
  Zachary Waldowski
  [email protected]


On Wed, Aug 9, 2017, at 06:23 AM, David Hart via swift-evolution wrote:> 
> 
> On 9 Aug 2017, at 09:21, Adrian Zubarev via swift-evolution <swift-
> [email protected]> wrote:>> Hi Jordan, is that only me or haven't you 
> metioned the default should
>> be applied to all new enums? Personally I'd say that 'closed' should
>> be the default and the 'open' enum would require an extra keyword.> 
> I think it should definitely be the other way round for public enums
> because closed is the stronger guarantee. Final is the default for
> classes because open is the stronger guarantee. That’s probably why we
> should not use the same keywords.> 
>> Now about the keyword itself. Here are two keywords that IMHO nail
>> their behavior down to the point:>> 
>> finite enum A {} - so to say a closed enum (default)
>> infinite enum B {} - so to say an open enum (requires default case in
>> a switch statement)>> 
>> If you think the default should be the other way around, than feel
>> free to switch that. 'finite' also implies that the enum connot ever
>> be extended with more cases (to become infinite), which was also
>> mentioned in your email.>> 
>> -- 
>> Adrian Zubarev
>> Sent with Airmail
>> Am 9. August 2017 um 00:27:53, Jordan Rose via swift-evolution (swift-
>> [email protected]) schrieb:>>> 
>>> 
>>> 
>>> Hi, everyone. Now that Swift 5 is starting up, I'd like to circle
>>> back to an issue that's been around for a while: the source
>>> compatibility of enums. Today, it's an error to switch over an
>>> enum without handling all the cases, but this breaks down in a
>>> number of ways:>>> 
>>> - A C enum may have "private cases" that aren't defined inside the
>>>   original enum declaration, and there's no way to detect these in a
>>>   switch without dropping down to the rawValue.>>> - For the same reason, 
>>> the compiler-synthesized 'init(rawValue:)' on
>>>   an imported enum never produces 'nil', because who knows how
>>>   anyone's using C enums anyway?>>> - Adding a new case to a *Swift* enum 
>>> in a library breaks any client
>>>   code that was trying to switch over it.>>> 
>>> (This list might sound familiar, and that's because it's from a
>>> message of mine on a thread started by Matthew Johnson back in
>>> February called "[Pitch] consistent public access modifiers". Most
>>> of the rest of this email is going to go the same way, because we
>>> still need to make progress here.)>>> 
>>> At the same time, we really like our exhaustive switches, especially
>>> over enums we define ourselves. And there's a performance side to
>>> this whole thing too; if all cases of an enum are known, it can be
>>> passed around much more efficiently than if it might suddenly grow a
>>> new case containing a struct with 5000 Strings in it.>>> 
>>> 
>>> *Behavior*
>>>
>>>  I think there's certain behavior that is probably not *terribly*
>>>  controversial:
>>>
>>>  - When enums are imported from Apple frameworks, 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 *not* be 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[1]" 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 [email protected]
>>> https://lists.swift.org/mailman/listinfo/swift-evolution>>> 
>> _______________________________________________
>> swift-evolution mailing list
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> _________________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution


Links:

  1. http://jrose-apple.github.io/swift-library-evolution/
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to