New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guards #371
Comments
Instead of the |
Unfortunately, this is not as easy. For example, if I write: fun foo(n: Int) = when (n) {
0, 1 -> "a"
otherThing() -> "b"
} it's not clear whether It could be possible to make a "better" transformation by checking the types, but I think that in this case having this translation hold before the type checking phase is a good thing to have. |
I would expect
Hence, any main condition in a when-with-subject that isn't preceded by |
While the idea behind this proposal makes sense to me, I don't think it translates into reality well. It ends up being shackled by some confusing rules that could just make the resulting code less clear rather than more. In the absence of exhaustiveness checking, the only real remaining motivation for the change is the argument that when-with-subject is more expressive and concise.
Actually I disagree with that argument. The proposed syntax: when (status) {
is Status.Ok && status.info.isEmpty() -> "no data"
...
} seems to me to be more confusing than just using a when {
status is Status.Ok && status.info.isEmpty() -> "no data"
...
} This is only a few characters longer than the proposed modification, behaves exactly the same, and requires zero additions to the language. If I want to make it clear that "status" is the subject, I can do so with status.let {
when {
it is Status.Ok && it.info.isEmpty() -> "no data"
...
}
} I understand the concerns that have led to the restricted version of the guard syntax in the proposal. But those restrictions are new rules to be learned, and to be honest, I already find the left hand side of a when-with-subject to be a confusing deviation from the ordinary rules of the language. The additional restricted guard syntax makes it more confusing. Without the benefits of exhaustiveness checking, I don't think this proposal can meet the minus 100 points bar. Even if exhaustiveness checks could be added, I worry they would become complex and hard to reason about. |
Although this proposal doesn't concern with exhaustiveness checking, we are pretty confident that if we implement this in the Kotlin compiler, the exhaustiveness would be correspondingly upgraded. |
Thanks, that's good to know! From the linked ticket, I gather the proposed improvements also extend to There's no need for two ways of doing the same thing. Once you add exhaustiveness checks, |
@roomscape I personally think when-with-subject has great potential to be a powerful pattern-matching construct. In its current form, it isn't yet, but I think it can get there, so I don't think it's a good idea to drop it entirely. |
I think this proposal makes sense only with exhaustiveness checking. As an android dev I write code similar to the provided examples quite often. And the reason I do sealed classes for states that are mutually exclusive is because I don't have to worry about adding a new subtype and forgetting about updating the when statement. |
Heavily agree with @roomscape 's first reply. In addition, I have three things
fun Status.renderNested(): String = when(this) {
is Status.Loading -> "loading"
is Status.Ok -> if(info.isEmpty()) "no data" else info.joinToString()
is Status.Error -> when(problem) {
Problem.CONNECTION -> "problems with connection"
Problem.AUTHENTICATION -> "could not be authenticated"
Problem.UNKNOWN -> "unknown problem"
}
} |
Indeed, in smaller examples it's arguable that the change is minimal, and as argued in the KEEP, the translation is also quite direct to non-subject version. However, this has the potential of steering the people towards a style where nested control flow is made clearer. Some use cases where I think guards shine:
private fun FirExpression.tryToSetSourceForImplicitReceiver(): FirExpression = when (this) {
is FirSmartCastExpression ->
this.apply { replaceOriginalExpression(this.originalExpression.tryToSetSourceForImplicitReceiver()) }
is FirThisReceiverExpression && isImplicit ->
buildThisReceiverExpressionCopy(this) {
source = callInfo.callSite.source?.fakeElement(KtFakeSourceElementKind.ImplicitReceiver)
}
else -> this
} |
If |
I'm concerned about using the existing token Using any other unambiguous token, including a new keyword or a new symbol combination, would be much more preferable. It will have to be learned by users, but that is a good thing. |
For exhaustiveness checking, I think a current hole would be classes like |
Unfortunately this is also the case currently with Kotlin, as data flow analysis works left-to-right. For example, |
Uniformly using |
Yes. This is why I mentioned "no side effects, no type information from the left is used on the right, etc". |
That is the problem. The natural syntax would be
which is just two letters more (or three if the subject is nullable) and does not need explanation. |
Or, for the lazy, it could be
But maybe that is too compact. |
Should the proposal ensure that less specific conditions should go after more specific ones? In other words, the following should better fail at compile time: when (str) {
is String -> println("String")
is String && str.isNotEmpty() -> println("non-empty String") // it is never reached
} |
Would like to note, when (charSequence) {
is String && charSequence.length > 5 -> ...
"MEOW" -> ...
else "meow" in charSequence -> ...
else -> null
} For elses, we can just not put a guard keyword at all. Oh, and another thing: IntelliJ should have the option of coloring the |
This is one of the reasons why I dislike |
The KEEP has been updated with sections on why |
This should be covered in principle by the reachability analysis in the compiler. |
We have preliminary results from our survey (which cannot be shared directly, as it contains some personal information). The comments, and the discussion that they've spawned within the Kotlin team, have pointed some weaknesses of the original proposed syntax with As a result, a new version of the proposal has been published, using |
While reading the initial proposal, I found that it might collide with future pattern matching features. As guards with The syntax |
The KEEP has been merged. Thanks everybody for their comments and suggestions! |
This is an issue to discuss guards. The current full text of the proposal can be found here.
This KEEP proposes an extension of branches in
when
expressions with subject, which unlock the ability to perform multiple checks in the condition of the branch.The text was updated successfully, but these errors were encountered: