http://try.kotlinlang.org/#/UserProjects/817cu2gf3lhui8kslloffunpdu/vds59e7n0dkhcoqspp9ge6j0ql
If when
expression result is not used, Kotlin doesnât check it for exhaustiveness (I think, Idea emits a warning, but not an error). You could use the following code to force a compilation error:
val unused = when (p) {
is Parent.First -> println("first")
}
Actually I donât understand the reasoning for this behaviour. IMO, when (obj)
must always be exhaustive.
Thanks.
IMO, when (obj) must always be exhaustive
+1
This really is a Kotlin pitfall!
There are plenty of cases where you need to handle just a few options out of all the possibilities and ignore the rest. We decided against requiring you to write something like else -> /* do nothing */
in such statements.
Weâve discussed the possibility of adding explicit syntax for âexhaustive whenâ but didnât see any option that we liked.
I think that current situation is good enough. Thereâs a warning for those who think that when
should be exhaustive (warning or error, it doesnât matter that much in practice) and itâs possible to disable a warning for those, who prefer not to add else -> {}
, so everyone can tweak this setting to his preference.
This is a missed opportunity IMHO.
There are plenty of cases where you need to handle just a few options out of all the possibilities and ignore the rest. We decided against requiring you to write something like else â /* do nothing */ in such statements.
Exhaustive matches make code safer, refactoring easier and speed up code reviews. Implicitly non-exhaustive matches only save one line of code. A line that Iâd argue actually has value as it signals to other developers that the author has consciously chosen not to handle every possibility.
Having worked with languages on both sides of the divide my opinion is that exhaustive matches are better in 100% of use cases.
All that leads to is reflexive adding of the else
clause. You can lead a horse to water but you canât make him think.
A warning is appropriate, an error is not.
I respectfully disagree, particularly because youâre only addressing the least valuable of the three benefits I mentioned:
- Makes refactoring safer and simpler by making the compiler work for you.
- Makes code reviewing
when
blocks simpler by eliminating the need to verify that all possible branches are handled in the cases where matching is exhaustive and no else block exists. (should be the majority) - Makes developer intent more clear as far as whether or not a block is meant to be exhaustive.
Itâs usage becomes a automatic only in an dysfunctional dev environment that does not enforce best practices in code reviews, just like converting a nullable to a non nullable using !!
would. In this case the best practice would be âDonât use an else clause without a very good reason.â
As an example:
when(animal) {
Dog -> alpha()
Duck -> beta()
}
You cant tell by looking at just this code whether it could or should handle more Animal types. If youâre doing a code review you then have to go find the definition of Animal to see if any other Animals exist, and then from there you can get on with determining whether or not the actual logic is correct.
Granted, the following is functionally equivalent:
when(animal) {
Dog -> alpha()
Duck -> beta()
else -> /* do nothing*/
}
The difference is that in an exhaustive environment, itâs a red flag to verify that else
is justified, just like using !!
operator should, since as a best practice, itâs usage would be an exception, not the rule.
Youâre ignoring or misunderstanding what Iâm saying. The benefits you claim arenât accrued becuase when a programmer is forced to write this, the else and non-else forms become equivalent. Nothing can be inferred from the else since most programmers are adding it becuase they know they have to avoid an error. Thus my comment about making the horse think. You canât force the programmer to think about the statement and theyâre more likely just to reflexively add the else. Just like those âAre you sure?â dialogs donât stop people deleting files they want to keep.
I agree that providing more information about intent is good, but forcing people to write code is not the answer, thatâs why I think a warning is appropriate. I think itâs just the right amount of nagging and generally programmers who care about warnings care enough to make sure all cases are arefully considered.
Youâre ignoring or misunderstanding what Iâm saying.
I donât think so. Youâve said that because it would be possible to get around exhaustive matching by including an else
clause that lazy programmers will always do so and is therefore not worthwhile, which is silly. By that reasoning we shouldnât bother distinguishing between nullable and non nullable or var vs val because lazy programmers can (and usually do) force unwrap nullables using !!
and use var where val would be more idiomatic.
The benefits you claim arenât accrued becuase when a programmer is forced to write this, the else and non-else forms become equivalent.
Using else
isnât compulsoryand as I've said the best practice would be to provide an exhaustive set of matches to eliminate the else whenever possible: using
else` is the exception not the rule. Just like it is with using guard let to safely use nullables or any of a hundred other non mandatory best practices, the team that leverages language features will benefit while teams that donât, wonât. Itâs not about making a language construct that forces people to be good developers but to give the good developers tools that make them even better.
However we should find a better alternative to
when {
...
}.let { }
like
when (b) {
Boolean.TRUE -> wow()
Boolean.FALSE -> damn()
!else
}
What about
when (b, exaustive = true) {
Boolean.TRUE -> wow()
Boolean.FALSE -> damn()
}
or even
exaustiveWhen (b) {
Boolean.TRUE -> wow()
Boolean.FALSE -> damn()
}
I found on some blog a really nice solution for the non-exhaustive when
statement.
val Any.exhaust : Unit
get() = Unit
fun test() {
when {
true -> println()
}.exhaust
}
So if you make it a rule in your project to use this for all side-effect when statements, the problem goes away.
would really love to see this as a language feature - so i opened a KEEP issue for it to maybe push it a bit: unreachable() · Issue #204 · Kotlin/KEEP · GitHub