When desperately needs `it`

This issue is being tracked here:
https://youtrack.jetbrains.com/issue/KT-4895

Feel free to vote!

2 Likes

Thanks for the link and I’ve voted in favour, though preferably without the need for val which seems to me to be superfluous.

1 Like

Yup I read up on that and noticed the same. It’s really too bad because it is the most natural thing to use here.

As a backup I wonder if we couldn’t do:

when (foo()) {
    bar is Bar -> println(bar.barProp)
    baz is Baz -> println(baz.bazProp)
}

(I added a comment to the feature request for this.)

Yes, it’s a pity that 'it' can’t be used because of the lambda problem.

I suppose you could use a different word such as 'that' or 'expr' but even this wouldn’t be perfect as there could still be problems if one 'when' was nested inside another.

So I think the best solution is to allow the programmer to choose an appropriate name for the subject variable.

Your own suggestion looks similar to the pattern-matching 'switch' statement in C# 7.0 where each 'case', if it represents a type, can be given its own variable. It’s another possibility for them to mull over but it’s a bit complicated for my taste - I’d prefer a single variable to be used for the whole 'when' which is what’s done in Go.

1 Like

Scala uses a similar format in its partial functions (case x if ... => ... ) . I was actually disappointed Kotlin didn’t have a Partial Function type because then you could easily have foo().when { is Bar -> println(it) }, and when wouldn’t even need to be a keyword, just an extension function.

You’re right that repeating the bound variable name is a little verbose, but there are times when you don’t need anything bound in every case. And TBH too many when cases seems like a smell that ought to look a little ugly anyway.

As for val…I’m actually not a fan of sticking val in there. It suggests val x = 0 is an expression when it’s not.

it in when would be a breaking change, so probably it won’t happen.

1.let {
  when (2) {
    2 -> println(it)
  }
}

This code now compiles and prints 1. With it in when it would print 2. Breaking change.

1 Like

I’d like to second this proposal since it is already overloaded.

1 Like

Is there anything wrong with the following:

foo().let {
    when (it) {
        is Bar -> println(it.barProp)
        is Baz -> println(it.bazProp)
    }
}
2 Likes

Nothing besides twice the indent and extra symbols to read. Less fluff is why we left Java, right?

  1. Wrong. It that was the case it wouldn’t be worth learning a new language. Kotlin is much much more than Java with less fluff.

  2. Here is the Java version. (195 characters vs 87 for the Kotlin version)

     Boo boo = foo();
     
     if (boo instanceof Bar) {
         Bar bar = (Bar) boo;
         System.out.println(bar.getBarProp());
     } else if (boo instanceof Baz) {
         Baz baz = (Baz) boo;
         System.out.println(baz.getBazProp());
     }
    
  3. Indentation is up to you. Here is a version with a single level:

     foo().let { when (it) {
         is Bar -> println(it.barProp)
         is Baz -> println(it.bazProp) } }
    

and if you prefer no indentation, you could even write it on a single line.

If you don’t want the feature that’s fine, don’t vote for it. But I’ve used bound variables on pattern matches in Scala and it always made for more concise, readable code.

Point 2 is a perfect example showing why Kotlin is better, and what I mean by “less fluff”. Type inference, smoother syntax, and so forth makes the code is easier to read, write, and debug. It’s a huge advantage over Java.

Suggestion 3 does not help. Indentation is shorthand for “here is a new multiline scope”. By collapsing the visual marker the code is more dense and harder to read. Any Kotklin reformatter will put the newlines right back where they should be.

Indeed, you can do it now at the cost of 1 single simple line :
val it = foo()
when (it) {
is Bar → println(it.barProp)
is Baz → println(it.bazProp)
}

IMOH the following code looks a lot worse
when (val it=foo()) {
is Bar → println(it.barProp)
is Baz → println(it.bazProp)
}

What I would have liked in Kotlin is a construct that would be better/more concise than

val a = A()
if (a.something()) return
val b = a.gah()
if (b.somethingOther()) return
val c = b.bouh()
if (c.somethingAboutC()) return
// do somerthing

But sometimes, we wish for impossible/performance degrading/useless things…

My point is that the extra line, in this case, is just noise. In fact I probably don’t need or want “it” in my scope after this construction is over.

To solve something like your other request, wouldn’t it be nice to use guards:

return when (foo()) {
    b is Bar if b.isSomething() -> 1
    b is Bar if b.isSomethingElse() -> -1
    z is Baz -> 0
    else -> -2
}

(EDIT: Added else clause)

That looks confusing as hell.

Maybe this is better Johnathan? And it works today.

val temp = foo()
return when {
    temp is Bar && temp.isSomething() -> 1
    temp is Bar && temp.isSomethingElse() -> -1
    temp is Baz -> 0
    else -> -2
}
2 Likes

What I would prefer is just to only allow it:

when (foo()) {
    it is Bar -> println(it.barProperty)
    it is Baz -> println(it.bazProperty)
}
2 Likes

If we can deal with nested lambdas, I’m sure we can deal with adding when to the mix. It almost looks like a lambda, anyway. (And it might be a function if we had hash literals.)

I think that variant is worse than the original.

Although I don’t think that using ‘it’ would be out of the question, you’d need to be aware of possible clashes with the use of ‘it’ not just in single parameter nested lambdas but also in such a lambda enclosing the ‘when’ statement.

Because of this, I’d be very surprised if they opted for that ( if indeed they decide to do anything at all) when the alternative of using a user-defined variable does not have these problems.

It’s not just “saving a line”; in highly-nested code using when’s return value, having to pre-assign the variable can split up code in ways that disrupt code readability and flow.

As a rather simple example of what I’m talking about:

    val content = someOtherCall()
    call(
        command,
        when(content) {
            is ObjectContent -> content.value
            is StringContent -> content.str
            else -> ""
        })

But the “it” version is an awkward ask that has too many style and substance things going against it. The syntax should be more similar to a for loop whether it be when(val foo = stuff()) or whatever.

1 Like