Skipped satisfied condition in when expression

So let me explain the context to see if I understand it correctly.

When we are using when expression without a subject, branch condition is satisfied when condition is true.

when {
    input is UUID -> "is UUID"
    isUUID(input) -> "is UUID in String"
    else -> "is somethin else"
}.also { println(it) }// "is UUID in String"

But when we are using subject in when, this no longer applies. The branch condition also has to correlate to the subject. If not, the branch is skipped even when after evaluation it is true.

when (input) {
    is UUID -> "is UUID"
    isUUID(input) -> "is UUID in String"
    true -> "is true"
    else -> "is somethin else"
}.also { println(it) }// "is something else"

If that’s the case, it would be nice if IntelliJ IDEA could gray out those branches with a warning message or it can lead to an unexpected behaviour.
I didn’t see this explanation in documentation.

Whole code:

import java.util.*

fun main() {
    val input: Any = "8777de13-b14c-4bed-83c0-0c8ab5cf0755"
    println(isUUID(input))// true
    
    // expected behaviour
    when {
        input is UUID -> "is UUID"
        isUUID(input) -> "is UUID in String"
        else -> "is somethin else"
    }.also { println(it) }// "is UUID in String"
    
    // unexpected behaviour
    when (input) {
        is UUID -> "is UUID"
        isUUID(input) -> "is UUID in String"
        true -> "is true"
        else -> "is somethin else"
    }.also { println(it) }// "is something else"
}

fun isUUID(uuid: Any): Boolean {
    return uuid is String && isStringUUID(uuid)
}

fun isStringUUID(id: String): Boolean {
    try {
        UUID.fromString(id)
        return true
    } catch (exception: IllegalArgumentException) {
        return false
    }
}

https://pl.kotl.in/pipk6bkjq

The branch condition also has to correlate to the subject. If not, the branch is skipped even when after evaluation it is true

To be more precise, if the condition written in when with bound value is an expression, the actual condition is an equality check between the bound value and the value of the expression.
This means in presented case, the condition checks if input is equal to true.

Note that the bound variable and expression in a condition must be compatible for the expression to be compiled. In your example, it is only allowed to have that condition, because input is of type Any, which can be compared to a Boolean.

See in the example below, that the condition with expression returning equal string is satisfied.
You can also modify the example, by changing the type of input to String, to see that the example no longer compiles, since Boolean cannot be compared to String.

import java.util.*

//sampleStart
fun main() {
    val input: Any = "8777de13-b14c-4bed-83c0-0c8ab5cf0755"
    
    when (input) {
        is UUID -> "is UUID"
        isUUID(input) -> "is UUID in String"
        true -> "is true"
        sameUuidValue() -> "is equal to value of the expression"
        else -> "is somethin else"
    }.also { println(it) }// "is something else"
}

fun sameUuidValue() = "8777de13-b14c-4bed-83c0-0c8ab5cf0755"
//sampleEnd

fun isUUID(uuid: Any): Boolean {
    return uuid is String && isStringUUID(uuid)
}

fun isStringUUID(id: String): Boolean {
    try {
        UUID.fromString(id)
        return true
    } catch (exception: IllegalArgumentException) {
        return false
    }
}

I didn’t see this explanation in documentation.

Indeed in documentation it is not stated explicitly, only implicitly in an example - note the x == 1:

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // Note the block
        print("x is neither 1 nor 2")
    }
}

It’s explained in more details in language specification

When expression with bound value (…) supports four different condition forms:
(…)

  • Any other applicable expression ( Expr ) The resulting condition is an equality check of the form boundValue == Expr .

(…)
Note: the rule for “any other expression” means that if a when expression with bound value contains a boolean condition, this condition is checked for equality with the bound value, instead of being used directly for when entry selection.

2 Likes