[swift-evolution] [Review] SE 0192 - Non-Exhaustive Enums

Jordan Rose jordan_rose at apple.com
Thu Dec 21 13:15:58 CST 2017



> On Dec 20, 2017, at 12:52, Colin Barrett <colin at springsandstruts.com> wrote:
> 
> 
>> On Dec 20, 2017, at 1:36 PM, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> wrote:
>> 
>> Thanks for the links, Colin. I think neither of these approaches are appropriate for Swift, largely for the same reason: they can't be used to define a library's API. Polymorphic variants are ad-hoc union types (much like tuples are ad-hoc structs) which—apart from having other problems in Swift previously discussed on this list—means that you can't add new cases to them. That is, any API which takes `(red(r: Int) | green(g: Int) | blue(b: Int))` today can't add `alpha(a: Int)` in the future, because that would change the type.
>  
> It would change the type yes, but not in a binary incompatible way. Imagine this for the OS version, using OCaml pseudocode
> 
> type VERS = [> `10_0 | `10_1 | … | `10_13 ]
> 
> Then, next year, you’d change VERS to be,
> 
> type VERS = [> `10_0 | `10_1 | … | `10_13 | `10_14 ]
> 
> Any code that dealt with a VERS would still work, as it had to handle that it could contain other tags.

Sorry, I had this worked out in my head but then didn't put it into the email correctly! The sticking point is that Swift allows overloads by type, which means that argument types have to go into the mangled name of the function. (Another way to think of this is to say "to uniquely identify a function across releases, you must know its argument and return types as well as its full name".) That means that we can't just add a new case into the type, because it would change the function's type.

Okay, but what if VERS were an actual new type rather than just a typealias? Well, then you have a non-exhaustive enum.

(David Owens brought up on Twitter today that it could be a different kind of declaration rather than 'enum'. My response <https://twitter.com/UINT_MIN/status/943896442472620032> was that that could work, but you end up in a worse case where (a) the two declarations are equivalent when they're not public, and (b) it's still easy to use the wrong one, especially going from Swift 1-4 habits.)


> 
>> ML-style exceptions have the opposite problem; they are completely unconstrained and so a client can add new "cases" without the library author being prepared. (Swift's error-handling model in general behaves a lot like this, except it doesn't get ML's knowledge of which errors might be thrown.)
> 
> Yes, I was imagining that this would be for something with an OCaml type like [> ]  or TOP, which I don’t believe is a thing? My OCaml-fu is quite weak.
> 
>> I'd sum this up by saying Swift is differing from ML and from most other languages because it is solving a different problem. Swift is designed such that the compiler does not require whole-program knowledge to produce a correct, working, type-safe program. It will use any cross-module knowledge it can for optimization purposes, but the language semantics do not depend on it. And this is critical, as you note, for libraries with binary compatibility concerns.
> 
> That is… not different from ML? ML’s modules have precisely this properly, do they not? Or am I misunderstanding what you’re saying here.

ML modules provide separation of concerns, but as far as I know they can't actually be swapped out at runtime, and if someone were to do this they wouldn't be allowed to add a new variant to an existing datatype.


> Thanks for the reply, it’s appreciated! Hope you’re well in California, envious of your weather trudging thru the snow here in NYC.

Happy holidays, Colin, and everyone else on the list. :-)

Jordan


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171221/cebdd409/attachment.html>


More information about the swift-evolution mailing list