When with "less than"

Is it possible to do something like this:

val x:Int = //bla bla
return when(x){
   < 1     -> "less than 1"
   1       -> "one"
   else    -> "greater than one"
}
13 Likes

The nearest I can get to that is the following though it’s not very elegant:

val x: Int = //bla bla
return when(x) {
   in Int.MIN_VALUE..0 -> "less than 1"
   1                   -> "one"
   else                -> "greater than one"
}

Personally, I’d just do:

val x: Int = //bla bla
return when {
   x < 1    -> "less than 1"
   x == 1   -> "one"
   else     -> "greater than one"
}
3 Likes

I second the need for this as well. I was wanting the same thing just this week.

I know you can do the long way with the temporary variable but it feels ugly and I don’t like it.

In another thread I proposed the ability to refer to the parameter to the when as “it” to use as a target of method calls as in:

when(string)
{
    it.contains("A") -> println( "A")

perhaps that could be extended to include relational expressions involving it:

when(x)
{
    it < 1 -> "less than 1"
9 Likes

I had a realization that it might be nice to do this with lambdas or function references:

E.g.:

when(string)
{
    { contains("A") } -> println( "A")

or

when(x)
{
    { it < 1 } -> "less than 1"

The current Kotlin approach looks cleaner, to my taste:

when {
    x < 1 -> "less than 1"
5 Likes

Doesn’t that nearly defeat the purpose of using a case based when statement?
Why does it support keywords such as “in” and “is”, but not operators? The syntax for that would be identical.

4 Likes

In cases when it is a simple variable that is cleaner. But with more complex expressions that you only want evaluated once, it requires storing it in a variable to do that. After a little while doing FP and Kotlin you get to the point where it just feels dirty to actually create a named local variable.

2 Likes

If you don’t want to assign an explicit name to your complex expression, then let is there to help you:

complexExpressionHere.let {
    when {
        it < 1 -> "less than 1"
5 Likes

That still defeats the purpose of a case based when statement…
With what you’re proposing, you might as well just use if statements and avoid the bloat of creating the when.

1 Like

I know there are other ways to work around it, and all that we are saying is that it would be really nice if the compiler didn’t force you to do that. If I have a when with a value it would be nice to be able to refer to it inside the when as it and use it in more complex conditionals. The when(x) statement is just rather limiting in its capabilities and it would be nice if it were more powerful so I can just say:

    when(complexExpressionHere) {
        it < 1 -> "less than 1"

Unrelated to using it in conditions, your version also brought up an idea I suggested a while back, the idea of having an “extension” when that is the equivalent of when(x) but expressed as x.when. It would be useful in chains to easilly sort of do a conditional mapping.

So for example now you have to use let like you did:

someComplexExpressionChain
     .let {
        when(it) {
            is Foo -> "Foo"
            is Bar -> "Bar"
            else -> throw Error()
         }
     }
    .additionalChainedOperations

My suggestion with the addition of a “.when” extension would allow you to express that as:

someComplexExpressionChain
     .when {
        is Foo -> "Foo"
        is Bar -> "Bar"
        else -> throw Error()
     }
    .additionalChainedOperations
2 Likes

Would that not add unnecessary complexity to how extensions work?
Unless what you are proposing is something within the actual language and not something users are able to create?

Yes it would have to be a language level change to support the extension style when. Its definitely not something that can be just implemented in a library.

any chances for this to be implemented? I often find myself in the need for it and need to find other less elegant ways around it.
Thanks

I found a bit hacky way that can help you in mixing greater than, less than, or any other expression with other in expressions.
Simply, a when statement in Kotlin looks at the “case”, and if it is a range, it sees if the variable is in that range, but if it isn’t, it looks to see if the case is of the same type of the variable, and if it isn’t, you get a syntax error. So, to get around this, you could do something like this:

when (foo) {
    if(foo > 0) foo else 5 /*or any other out-of-range value*/ -> doSomethingWhenPositive()
    in -10000..0   -> doSomethingWhenBetweenNegativeTenKAndZero()
    if(foo < -10000) foo else -11000 -> doSomethingWhenNegative()
}

As you can see, this takes advantage of the fact that everything in Kotlin is an expression. So, IMO, this is a pretty good solution for now until this feature gets added to the language.

I found another hacky way of doing this, taking advantage of the fact that in is allowed and calls a method you can override (contains). For the example above:

interface WhenComparator<T> {
    fun test(other: T): Boolean
    operator fun contains(other: T) = test(other)
}

fun <T> lt(value: T) = object: WhenComparator<T> {
    override fun test(other: T) = other < value
}

when (x) {
    in lt(1) -> "less than 1"
    1 -> "one"
    else -> "greater than one"
}

I’d prefer something overridable that’s not in though, since that has a well-defined meaning and this sort of hack is beginning to look too much like C++!

4 Likes