I'd like to discuss a processing of nonexhaustive external enums(imported from
separate module/framework) a little more.
I understand that we (in most cases) don't want to exhaustively check some C's
declared enum which is used for some system's API etc.
But what about 'usual' libraries distributed as framework/module?
Do you agree that:
1. In most cases external enums(public enums declared in *separate module*) will be
nonexhaustive, because library developer don't want to crash consumer's code if later
will be decided that one more enum case is needed in that public enum.
Enums like Optional<T>, that should be exhaustive, will be rare in compare to "usual"
enums like ButtonType, PaymentMethod, LoginKind etc
2. So, almost any enum coming from separate module/framework, even if library
designer *wants* user's code to process that enum exhaustive(and it is perfectly
logically to process it exhaustively in some places of user's code), will be declared
as 'nonexhaustive' *just* to preserve binary compatibility if new case will be added.
3. So we actually will lose a compiler's help to work with most of such
external enums.
4. If you ever need to support a code with exhaustive switch regarding external
'nonexhaustive' enum(and as noted above, most of such enums will be nonexhaustive) -
you are in a big trouble, you'll need to manually check for each newer version of
used library(module) if there are new cases in used enums for each switch in your
code, or try to find a 3rd party tools that will help with this. But given you have
'default' in your switch - how the tool can say that you want tot be exhaustive here?
?
Also, please consider the two examples below. Is there incorrect logic/assumption
used in any of them?
1. Let's say I have a Shapes enum in my library, which is distributes as separate
module/framework. Each shape will have its own properties(like radius for .circle,
side length for .square, angle and lengths for .line etc). Will I defined it as
'exhaustive' ? No. It is very possible, that I'll add some cases into the enum and I
don't want to break user's code. Is it possible that user's code want to switch
exhaustive on this enum? Of course, but there is no support for this in compiler.
Can user's code work with new(future) enums? It depends, but *this is possible*, for
example it can process only known cases, and show some warning like "unknown items
found, please update to latest version of app" instead of crash.
2.
* I'm using a framework(separate module) that exports Things enum.
* This framework provides user's code with [Things] array
* I show each 'thing'(let's say its 'title' property) from that array in TableView
per row
* By selecting a row, I show a details page for selected thing. Information on
details page depends on associated value for the selected 'thing'
* For unsupported(future) 'things' I'll show only title for it in TableView and
'Unsupported' marker. Details page will not be available for it, until I update the
program. When selecting it, alert will show something like "Unknown type of thing.
Please update the app to support new things".
* Is it required for me to be able to exhaustive switch of such enum? Yes, I need
this to process all known cases in Details page. Exactly why we need exhaustive
switch for 'internal' enums. But no compiler's help in this case.
I just want to say, if I understand correctly, that enum declared in separate
module/framework will usually be 'nonexhaustive' even if library developer expects
user's code to exhaustive switch on that enum. And that we really need a way to be
exhaustive in switch in our code regarding imported enum which is declared as
'nonexhaustive' in its module.
Thank you for attention and your time.
Vladimir.
On 18.09.2017 20:23, Jordan Rose via swift-evolution wrote:
On Sep 16, 2017, at 15:35, Kenny Leung via swift-evolution
<swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
In general, I agree with everything in the proposal.
I’d like to propose these alternative extensions for clients:
1) As a client of an enum, I’d like to know in the future when a new value has been
added to an enum, since I may have to do something about it. How about adding the
“exhaustive” keyword to be used in the switch statement? Like
exhaustive switch excuse {
case eatenByPet:
// …
case thoughtItWasDueNextWeek:
// …
default:
// …
}
If exhaustive is used, there would be a warning if all cases aren’t covered *even
though default exists*. This means that I as the client thought I had everything
covered when I wrote this code.
As already mentioned, this makes the default case un-testable, which brings me
to
2) All non-exhaustive enums should have the pseudo value “default” that can be used
just like a regular value. This would allow you to write code like:
teacher.failedToHandInHomework(excuse: .default)
which would allow you to trip the default case in any code you may write.
Brent came up with this idea as well, but after thinking it through Joe Groff and I
found that it doesn't really work in practice:
It’s going to be very common to have a future value and hand it right back to the
framework without looking at it, for example:
override func process(_ transaction: @testable Transaction) {
switch transaction {
case .deposit(let amount):
// …
case .withdrawal(let amount):
// …
default:
super.process(transaction) // hmm…
}
}
So just making it to the ‘default’ case doesn’t guarantee that it’s testable in
practice.
I'll add the actual code here to the "Testing invalid cases" section under
"Alternatives considered", since that's a clearer example than the paragraph I wrote.
(Oh, and the "exhaustive switch" is equivalent to the section on "'future' cases".)
Thanks for the feedback!
Jordan
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution