Comparison operators in when expressions

I’m aware that I’m not the first person to bring this up, but all discussions on the matter I’ve found are several years old.

When writing a when statement, it’s often the case that we’re dealing with numeric values (or generally: Comparable<T>s). However, a when case in Kotlin currently cannot use any comparison operators. For example, the following sadly doesn’t work:

val myInteger: Int = randomInt()
val description = when(myInteger){
   < 0 -> "negative"
   0 -> "zero"
   > 0 -> "positive"
}

I’ve seen various workarounds, but they’re all very unsatisfying. Using in and is is acceptable, but comparison operators somehow are not?

Here are a couple of workarounds (I deem none of them acceptable):

// problematic: "myInteger" is clearly the subject of the "when" expression, 
// yet the "when" is stated to have no subject.
val description = when {
   myInteger < 0 -> "negative"
   myInteger == 0 -> "zero"
   myInteger > 0 -> "positive"
}
// lipstick on a pig, doesn't solve the underlying problem
val description = myInteger.let {
   when {
       it < 0 -> "negative"
       it == 0 -> "zero"
       it > 0 -> "positive"
   }
}
// this one actually does the trick, but readability is poor
val description = when(myInteger) {
   in Int.MIN_VALUE ..< 0 -> "negative"
   0 -> "zero"
   in 1..Int.MAX_VALUE -> "positive"
}

Can we please reconsider that it might be a good idea to have comparison operators on when subjects?

3 Likes

You can create some named ranges to simplify for this case:

    val Int.Companion.negative get() = MIN_VALUE..<0
    val Int.Companion.positive get() = 1..<MAX_VALUE

    val Int.description get() = when(this) {
        in Int.negative -> "negative"
        in Int.positive -> "positive"
        else -> "zero"
    }

But in general, your point is valid

2 Likes

@dalewking that’s a new one, haven’t seen that solution before! :slight_smile:

However, as shown in the opening post, I’d like the exhaustiveness check to work on it as well without using “else”. And I think for this construction it would be very difficult for the compiler to verify that because of the indirection.

1 Like

I guess it could be a parser restriction.

    when (x) {
        0 -> -1
        < 0
        + 1 -> false
    }

could read

    when (x) {
        0 -> -1 < 0
        +1 -> false
    }

or

    when (x) {
        0 -> -1
        < 0 + 1 -> false
    }

Interesting enough, the compiler will accept

    if (x
        > 2) println(true)

although the kotlin grammar says
image
thus only allows a LF after the comparison operator and will reject the when example stated above.

1 Like

@tango24 if there is an ambiguity, this wouldn’t be the first place in the kotlin syntax where the presence or absence of a newline character makes a difference. I would happily accept that.

Another option (a bit Scalaish) is to have sealed classes like “PositiveInt”, “NegativeInt”, “Zero” (or better domain-specific like “StockGrowth”/“TemperatureRaise”/whatever).

Then in your code base you don’t need such “whens” at all because you are guaranteed to have a value which belongs to a specific case. Then use some kind of polymorphism to switch between cases.