Cant break out of lambda


#1

Hi Team,
I am trying to add an very useful extension method to Java’s InputStream class, since we know stream parsing requires several lines of boiler plate code and In my application we need to process stream multiple times.
so far my extension function works,
But it is begin really very useful for some of the drawback we are facing with kotlin core language feature.

I would suggest the core team to try this and provide some solution.

My extension Function to Java Stream accepting single argument method definition

fun InputStream.forEachLine(consumer: (line: String)->Unit){
    val reader = BufferedReader(InputStreamReader(this, Charset.defaultCharset()))
    var line: String? = null
    line = reader.readLine()
    while (line != null) {
        consumer(line)
        line = reader.readLine()
    }
}

//My Test is here
@Test
fun testExtnInputStreamForEachLine() {
    val stream = FileInputStream(File("c:\temp\sometextfile.txt"))
        stream.forEachLine {
            println(it)

            if(it.equals("some text")
            // I want to break the whole forEachLine block 

        }
}

In above example I have tried everythis.

  • return@forEachLine (this is working good for skipping the same block processing, similar to continue)
  • created a run block with label and tried with return on it. (gives compile time error)
  • break@withLabel (compile time error)
  • changed method returning boolean instead of unit and tried returning false (compile time error)

Team Please help


#2

Your extension is not inline so the return will just return to the loop over your lines. Btw, there is already some code in the standard library that does almost what you want: Reader.forEachLine. That does close the reader though (there are some variants you may want to look at).


#3

Hi @pdvrieze,
Thanks for the quick response, yes you are right I am trying me make the similar function but not the same function.
again in Kotlin’s Reader.forEachLine there’s no way i can exist the loop, I am new to kotlin, can you please help me to use this in a right way.
Basically while processing my stream I want to stop processing in-between if required conditionally.


#4

You can define your lambda as (line: String) -> Boolean that way you can return either true(continue) or false(break). That just leaves you with the problem that you always have to return true at the end of the lambda.


#5

It does not allow me to return false inside the lambda, I have mention this in my question, but yet this works if I am not passing function reference while calling this extension instead of passing function definition directly as lambada.

btw, I have figured out a way.

(line: String, loop: Loop) -> Unit

I am using a custom Loop class with single property called next : Boolean
user can set this to false and loop can break’ed

e.g:

class Loop {
    var next: Boolean = true
}

/**
 * @param consumer set loop.next to false to break the loop 
 *
 */
fun InputStream.forEachLine(consumer: (line: String, loop: Loop) -> Unit) {
    val reader = BufferedReader(InputStreamReader(this, Charset.defaultCharset()))

    var line: String? = null
    line = reader.readLine()
    val loop =  Loop()
    while (line != null) {
        consumer(line, loop);
        if(!loop.next){
            break;
        }
        line = reader.readLine()
    }
    reader.close()
}


@Test
fun testExtnInputStreamForEachLine() {
    val stream = FileInputStream(File("c:\\somefile.txt"))
        stream.forEachLine{
            line, loop ->
            println("additional line")
            loop.next = false //and this works 
        }
}

#6

For inline functions you can return to the block in which the lambda function was defined (the block that receives the inline code). Mark your initial code as inline and you will get different results.


#7

Perfect, this is working great,
But now I again have to read and understand what inline is and what all impact it can bring to my code. can you please point me to some very easy explanation and usage of inline function other then internal performance related explanation provided by kotlin documents online

Not sure How to accept this answer as a solution in this forum post, but this is the right solution for me in this situation.


#8

Making a function inline tells the compiler “Wherever this function is called, replace the function call with the actual code of the function”. You can think of it a little bit like a macro, the resulting bytecode will look like you wrote the code of the function directly where you actually wrote the call to the function.
Not sure if that explanation is clear enough, so I’ll add an example:

fun helloWorld() {
    println("Hello World")
}

inline fun helloWorldInlined() {
    println("Hello World")
}

fun main(args: Array<String>) {
    helloWorld()
    helloWorldInlined()
}

If you compile this code and then decompile it again, the main function will look like this (simplified):

fun main(args: Array<String>) {
    helloWorld()
    println("Hello World")
}

The second function call is gone, because it was inlined, i.e. the code of the function inserted directly in place of the function call.

Aside from performance considerations, this enables a few use cases that would otherwise be impossible, like non-local returns (this is what you needed here) as well as reified generics. It allows the code to use information that would not be available anymore at runtime with an ordinary function call.