Anonymous code blocks in Kotlin (versus Java)


#1

Hello

I have a question about how anonymous code blocks are handlied in Kotlin.

When programming in Java, I often use anonymous code blocks as a way of restricting the scope of local variables. This mechanism is used a lot in Java sourcecode that I am generating, and am looking for a Kotlin equivalent.

So, in the following block, I can re-use param1, param2, param3 multiple times.

public static void main (String[] args) {
   {
      int param1 = myFunc(1);
      int param2 = myFunc(2);
      int param3 = myFunc(3);
      doSomething(param1, param2, param3);
   }

   {
      int param1 = myFunc(4);
      int param2 = myFunc(5);
      int param3 = myFunc(6);
      doSomething(param1, param2, param3);
   }   {
      int param1 = myFunc(7);
      int param2 = myFunc(8);
      int param3 = myFunc(9);
      doSomething(param1, param2, param3);
   }

}

Now, when I try this in Kotlin (I won’t post the Kotlin snippet as it’s so trivial) , I receive the following message…

Multiple markers at this line - The lambda expression is unused. If you mean a block, you can use ‘run { … }’ - 1 **
** changed line

This sounds like it’s calling the block as an anonymous function, which has two problems. One is that I get a lot of warning everywhere, and two is that I imagine that the performance is much lower than in native java code.

What I would like to know is, is there a Kotlin equivilent of local scoping / anonymous code blocks that has an identical performance profile. And, will it remove the yellow warning markers from my code…


#2

As the warning you copied stated, you want to use:

run {
  val param1 = myFunc(4)
  doSomething(param1)
}

Run is an inline function so the code will actually revert back to being equivalent to a code block, return will even work as expected (and finish the containing main function, not the lambda). Now, in many cases you don’t need this. If you have a single variable that you need to do something with let is an interesting approach (especially for getValue()?.let { v -> print(v) }), you can nest let’s but that gets ugly very quickly.


#3

Thanks, I can see that run { … } does work the same way that Java treats these anonymous blocks, but I’m not terribly familiar with the way that these blocks are dealt with on the JVM.

Assuming “run { … }”, I really want to know that if I have code that uses lots of blocks like this in tight loops, will the performance profile be identical to the Java equivalent, all things being equal.


#4

Basically yes. A JVM code block is basically just a compiler helper. It tells the compiler that the local variables declared within the block (and their stack positions) are no longer used (and can be reused for something else). There is a slight advantage to lower position local variables but not massive.


#5

Hello,

Any reason not to support anonymous blocks and require run?

Basically, I’d like to write:

val parser = matchingParsers[0] ?: {
	LOG.info("Log something here")
	return null
}

parser.parse(...)

instead of:

val parser = matchingParsers[0] ?: run {
	LOG.info("Log something here")
	return null
}

parser.parse(...)

Please let me know if there is a better idiom I should be using

Thanks!


#7

This way anonymous code blocks would be indistinguishable from lambda expressions:

val result = { a + b }

Currently this is parsed as lambda function { a + b } being assigned to the variable result rather than the result of code block a + b execution.


#8

Makes perfect sense, thanks!


#9

How could you deal with the following use case?

val iter = list.iterator()
while (iter.hasNext()) {
    val a = iter.next()
    a ?: {iter.remove();continue} // remove null element and continue loop.
    //do something else
}

{iter.next();continue} is illegal and cannot be replaced by run { iter.next();continue }


#10

@wumo I think that situation is a bit forced since there are better alternatives.
Someone could simply use:

if (a == null) {
    iter.remove()
    continue
}

But most likely they’d take a different approach entirely. The continue in a loop, manually using an iterator, and implementing a simple not-null filter are all good indicators to redesign.

I’d expect most people to use some form of steam operators in that situation:

list.filterNotNull().forEach {
    // do something else
}