[swift-evolution] Enums and Source Compatibility

Vladimir.S svabox at gmail.com
Thu Sep 7 11:55:31 CDT 2017

On 07.09.2017 18:23, Rod Brown wrote:
>> On 8 Sep 2017, at 12:24 am, Vladimir.S <svabox at gmail.com> wrote:
>> On 07.09.2017 16:03, Rod Brown wrote:
>>>> On 7 Sep 2017, at 9:26 pm, Vladimir.S via swift-evolution
>>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote: Just
>>>> small note. As I understand, this is a source breaking suggestion, no? I
>>>> mean any client code for 'public enum' coming from another module, will have
>>>> to add 'default' case in 'switch'. So, the previously correct code will not
>>>> compile. Or do you suggest to raise a warning only for the first time and
>>>> raise an error in next version of Swift?
>>> Yes. By defaulting to “non fragile” we add a source incompatibility, because
>>> Swift is currently assuming fragility. In Jordan’s proposal, it is assuming
>>> fragility/exhaustive by default, and therefore would be source compatible.
>>> Good point. I keep wavering back and forth on the importance of Source
>>> Compatibility with this one. Defaulting to “exhaustive” seems dangerous to me.
>>> It makes your framework (and future versions of it) fragile without ever
>>> having to think about it. At least if the keyword of “exhaustive” or “fragile”
>>> or “sealed” meant that you actively chose the fragility that will handcuff you
>>> later down the road.
>> Yes, probably(still thinking about this) I agree that 'open'('exhaustive') enum
>> should be the default for public enum; and only if author is really sure enum
>> will not change in future and to provide a space for compiler's optimizations
>> and suggest client to switch exhaustive, he/she can mark such enum as
>> exhaustive/fragile/sealed/closed/fixed. And seems like the right direction is
>> raise warnings fist, and errors in next version of Swift.
>> The only question I can't find strong answer for, what my code(as a client of
>> 'open' enum) will do in 'default' case? Will it be in 95% cases the
>> fatalError().. If so, what is the difference with current situation? If my
>> switch is already exhaustive regarding some public(imported) enum, so I'm
>> processing all the possible *at the compilation time* cases - most likely I'll
>> have just fatalError() in 'default'. If my switch already contains 'default' -
>> the proposed change will not affect me at all…
> This was discussed earlier. As I mentioned, we should be realistic: it’s rarely a
> truly fatal error when you get an unknown enum. Unless you really can’t find a
> reasonable default, then there are almost always reasonable logical ways to handle
> it (perhaps just breaking out?). And in cases where you really need to handle each
> case, this would (almost?) always be an exhaustive enum. I can’t even imagine the
> case where it would be so critical you couldn’t handle it with reasonable control
> flow. There are always solutions like returning early, breaking out, default
> details. I see Swift code littered with fatalError() not because its needed, but
> because the developer often didn’t care enough to think it through.

Thank you for replies, Rod!

Thinking about this, if "in cases where you really need to handle each case, this 
would (almost?) always be an exhaustive enum", then I tend to agree with you.

So, currently my point of view is that we need to move the way suggested by Chris, 
when we need to mark public 'closed' enum, so 'open' is default, and with warning for 
first time and with error in next version of Swift for switches on 'open' enum 
without 'default' case. Not the way described in the proposal itself.

>> But probably it is better to have a choice what to do with future cases, than
>> have just one direction - crash at run time if new case in external enum is
>> added.
>> But the same I can say for proposed 'future' case : it is better to have a
>> choice what to use - 'default', if we don't need to be exhaustive on 'open'
>> enum,  or 'future', if we need to be exhaustive on 'open' enum in compile-time
>> but separately process all future cases.
> “Future” might be a decent idea, but I think we’re overcomplicating it. Just use
> “default” as we always have? “Future” as a word doesn’t seem to apply, as I have
> mentioned earlier: there are cases where your framework has a private case on an
> enum. This isn’t necessarily a future case - it could be “current”. If we were to
> do this, it would make more sense to call this case word “other” instead. Again,
> though, we’re adding complexity to a switch for a very rare case and a little bit
> of developer convenience in that case.

Well... yes, seems like this. I didn't get current but private cases into account. 
But I was more focused on Swift's enums, not on C's.

What do you(and others) think about such idea: if we have an exhaustive switch on 
_open_ enum _without_ 'default' case, and there are *no* more known available cases 
for this enum currently - compiler generates error/warning like "you have exhaustive 
switch but this is an open enum new cases can be added later, so add 'default' case 
to process them". But, if such(exhaustive on _open_ enum _without_ 'default' case) 
switch contains not all known cases, warning/error will be different like "you have 
not exhaustive switch on open enum, add 'default' case to process all others and 
future cases".

I believe that with this accepted proposal, if you ever will need to keep switch 
exhaustive on 'open' enum, support this code, this will be a nightmare without at 
least such warnings, when you can comment the 'default' case in switch and check(by 
warning/error message) if you processed all the cases or missed some. Otherwise, the 
alternative, is to manually check one-by-one what you have in your switch and what 
cases are in enum(in case you have new version of enum's module).


>> Yes, in this case we have a problem with testability, but the same is true for
>> exhaustive switch with 'defailt' case - how this 'default' code can be tested?
>> Probably, we need to provide a solution for this in any case, not because of
>> 'future' but also for 'default' in exhaustive switch on 'open' enum. (Don't know
>> how this could be implemented, probably by somehow be able to extend the
>> imported open enum with 'fake' case when compile in special mode for testing and
>> be able to send this fake case into tested code)
> Yes, testing this is a concern. I agree: have an ability to add an “other" case in
> tests.
>> Vladimir.
>>>>> 3. fragile public enum: cases may not be added, because that would break
>>>>> the fragility guarantee.  As such, clients within or outside of hte
>>>>> current module may exhaustively match against the enum.
>>>> I think 'fragile' word does not reflect what is guaranteed for this enum.
>>>> The author guaranteed that this enum will not be changed, not "this enum can
>>>> broke your code". Can't we use 'sealed'/'closed'/'fixed' here?
>>>> Vladimir.

More information about the swift-evolution mailing list