[swift-evolution] Handling unknown cases in enums [RE: SE-0192]
Jordan Rose
jordan_rose at apple.com
Tue Jan 16 12:25:11 CST 2018
> On Jan 14, 2018, at 09:36, Vladimir.S <svabox at gmail.com> wrote:
>
>
>
> On 12.01.2018 21:38, Jordan Rose wrote:
>>> On Jan 12, 2018, at 06:49, Michel Fortin via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>
>>>> Le 12 janv. 2018 à 4:44, Vladimir.S via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> a écrit :
>>>>
>>>> On 12.01.2018 10:30, Chris Lattner via swift-evolution wrote:
>>>>>> On Jan 11, 2018, at 11:15 PM, Jean-Daniel via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>>
>>>>>> A question about the new #unknown behavior. Is it intended to be used for error handling too ?
>>>>>> Will it be possible to use in catch clause ?
>>>>> If we go with the #unknown approach, then yes of course it will work in catch clauses. They are patterns, so it naturally falls out.
>>>>> If we go with the “unknown default:” / “unknown case:" approach, then no, this has nothing to do with error handling.
>>>>> IMO, this pivots on the desired semantics for “unknown cases in enums”: if you intentionally try to match on this, do we get a warning or error if you don’t handle all the cases? If we can get to consensus on that point, then the design is pretty obvious IMO.
>>>>
>>>> For me the other question is what "all the cases" means for enum with private cases(if we'll have them). I.e. if switch contains all the "public" cases of frozen enum - does this mean "all the cases" were processed? As I understand, the answer is no, because we *can* have 'private' case value here and so we need to react to this. How switch will look in this case?
>>>>
>>>> switch frozenEnumWithPrivateCases {
>>>> case .one: ..
>>>> case .two: ..
>>>> unknown default: .. // or 'case #unknown:' depending on our decision, or 'unknown case:' etc
>>>> }
>>>> ?
>>>> But then such switch looks exactly as switch for non-frozen enum value, no? It looks like we are reacting on future new cases, while enum is frozen.
>>>>
>>>> Moreover. How the switch for non-frozed enum with private cases should looks like?
>>>>
>>>> switch nonfrozenEnumWithPrivateCases {
>>>> case .one: ..
>>>> case .two: ..
>>>> unknown default: .. // or 'case #unknown:' depending on our decision, or 'unknown case:' etc
>>>> }
>>>> ? But then, is that 'unknown default' for reacting on "future" cases we didn't know about during the compilation OR it is for reacting on private cases?
>>>>
>>>> Or the main idea that we don't want to separate "future" cases and "private" cases?
>>>
>>> I think treating both as the same thing is the right idea. You also need to handle "future private" cases and "private cases that become public in the future". These are all unknown cases in the context of the switch.
>>>
>>> So an enum with private cases can't be switched exhaustively outside of its module. Thus, @frozen would need to forbid private cases... or we need @exhaustive to forbid private cases so they can be allowed by @frozen.
>> As mentioned in "Future directions", my recommendation to anyone planning to write a proposal for non-public cases is to go with the former, which would keep it from infecting the design.
>
> Thank you for the comment!
> From proposal:
> "Were such a proposal to be written, I advise that a frozen enum not be permitted to have non-public cases."
>
> OK. Seems logically for frozen enum(imported from another module) to not have non-public cases, as such cases most likely will be added later during the evaluation of the library(external module) - so such enum should not be frozen then.
>
> I'd like to discuss how current decision will fit into the (possible) future 'private cases' in enum.
>
> 1. Non-frozen enum with private cases in the same module.
>
> It seems like we'll need to write
> switch val {
> case .one : ..
> case .two : ..
> unknown default: .. // for private cases, even 'val' can't have 'future' cases
> }
>
> So, 'unknown default' will mean not just 'future cases', but 'future cases and private cases'.
Interesting. I didn't think about that; in the original proposal without any warnings, you just had to use 'default'. But it does make sense, and whoever proposes private cases will have to mention that.
>
> 2. Non-frozen enum with private cases in another module.
>
> In this case, if we want exhaustive switch, we need to use 'unknown default'. But I feel like known but private cases are not the same as future public cases for the 'consumer' of that enum, no?
>
> I mean, when making a decision what to do inside 'unknown default' - isn't it important to know what is the "event" - new public case or private(even "known") case? I'm thinking about something like this:
>
> let val = getSomeNonFrozenEnumResultFromLibrary()
> switch val {
> case .one : ... // process the val on our side
> case .two : ... // process the val on our side
>
> private default : sendValueBackToLibraryToProcess(val) // not interested, calling some special handler
> future default : .. // somehow react on important new case introduced in library. for example, show "please update the app" for the user and cancels the current operation
> }
>
> I don't think we need to think about "future private" cases, as we don't have access to them in any case, so current and future private cases are not distinguishable for us.
>
> I'm not sure if we should distinct future cases and private cases on 'switch' side, but I think we should discuss this now for taking a correct decision regarding 'unknown default' etc.
I think this distinction is both too fine-grained to be useful and not possible to implement. You don't get to see whether an enum has private cases or not, so every switch on a non-frozen enum from another module would have to have both of these, and then when the library is actually updated but your app isn't recompiled, you can't tell whether the case you're just now seeing is a new private case or a new public case.
>
> P.S. FWIW I agree with Chris Lattner that 'unknown default' fits into Swift syntax and mental model much better than 'unknown case'. This handler is for default reacting on _number_ of unknown cases, not for reacting on some _specific_ case, like other 'case xxx:' handlers. Are we going to discuss this and select the better name? Or most of us not agree that 'unknown case' is the best?
*shrug* That's on the core team to decide when the second review period is over. Nobody has convinced me yet that `unknown default` is better than `unknown case`, but the particular spelling here isn't a key part of the proposal. (I just replied to Chris with my reasoning for preferring `unknown case`.)
Jordan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20180116/c618ae9f/attachment.html>
More information about the swift-evolution
mailing list