When desperately needs `it`


#1

Am I missing something? I want to do something like:

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

except it isn’t present of course, so I have to do something clumsy like

val f = foo() // local variable and assignment I don't otherwise need
when (f) {
    is Bar -> println(f.barProp)
    is Baz -> println(f.bazProp)
}

Receiving parameters in when statement
When property access
Give when implicit access to the value under consideration
Give when implicit access to the value under consideration
Make using when-keyword more concise (or provide alternative)
Give when implicit access to the value under consideration
Receiving parameters in when statement
#2

‘Subject variable in when’ is one of the features considered in the recent future features poll. If it were to be implemented, the most likely syntax is:

when (val f = foo()) {
    is Bar -> println(f.barProp)
    is Baz -> println(f.bazProp)
}

The problem with using 'it' to refer to the subject variable is that it might lead to confusion if there was a lambda expression within the when statement.


#3

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

Feel free to vote!


#4

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


#5

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.)


#6

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.


#7

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.


#8

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.


#9

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


#10

Is there anything wrong with the following:

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

#11

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


#12
  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.


#13

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.


#14

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…


#15

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)


#16

That looks confusing as hell.


#17

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
}

#18

What I would prefer is just to only allow it:

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

#19

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.)


#20

I think that variant is worse than the original.