Why do non-exhaustive when expressions cause compiler error, but when statements don't?

Viewed 845

If I have a sealed class, for example

sealed class Result {
    object Success: Result()
    object Failure: Result()
}

Why do when statements compile when they are not exhaustive, but when expressions don't compile?

        when(getResult()) { //  `when` doesn't show a red squiggly, compiles file
            Result.Success -> {}
        }
        
        val result = when(getResult()) { // <--- when shows a red squiggly, doesn't compile
            Result.Success -> {}
        }

To make it a compile error for when statements, just call the exhaustive extension property:

        when(getResult()) { //  `when` NOW shows a red squiggly            
Result.Success -> {}
        }.exhaustive

I was watching YouTube: Sealed classes - Kotlin Vocabulary where they suggested using the following code to overcome this, but I want to understand why there is a difference. That should help me understand why the following Kotlin generic extension property solves the problem.

val <T> T.exhaustive: T
        get() = this

EDIT: Thanks to Tenfour04, I know why. I just don't know why this exhaustive extension function does its job.

2 Answers

What would you expect to be assigned to result in the case where the result is not Success? The point of an expression is to evaluate to something. If you have cases where it doesn't evaluate to something, it would be like a function that has branches that don't return anything.

A statement doesn't have to return anything, so there's no reason it should be forced to. It's just like an if statement with no else branch in languages like C or Java. It's for when you want to do certain tasks only under certain conditions.

By calling something (a property in this case) on the result of a when(), you force the compiler to treat is as an expression instead of a statement. A statement doesn't evaluate to anything, so you can't call anything on its non-existent result.

The downside to this way of forcing exhaustive statements is that it creates a property that will appear in the autocomplete of absolutely everything. You could alternatively tag it with an empty scope function, but that looks kind of ugly.

when(getResult()) {
    Result.Success -> {}
}.run { }
Related