0

I'm using the buildSequence function in Kotlin. How do I end the iteration in the middle of the function? I'm looking for something similar to C#'s yield break statement.

My code looks something like the following. I'm stuck at the TODO.

fun foo(list:List<Number>): Sequence<Number> = buildSequence {
    if (someCondition) {
        // TODO: Bail out early with an empty sequence
        // return doesn't seem to work....
    }

    list.forEach {
        yield(someProcessing(it))
    }
}

EDIT

Apparently, I misdiagnosed the source. The issue is not returning from the buildSequence function. The following works for me:

fun foo(list:List<Number>): Sequence<Number> = buildSequence {
    return@buildSequence

    list.forEach {
        yield(someProcessing(it))
    }
}

EDIT 2

The issue is that I put the return in a local helper function that validates data at multiple points in the buildSequence (Hence the helper function). Apparently I'm not able to return from buildSequence within the helper function. The error message was not terribly helpful...

Mashmagar
  • 2,556
  • 2
  • 29
  • 38
  • To achieve long-range action in aborting the sequence generation, you can use exceptions. Throw a validation exception from the helper function and wrap the whole `buildSequence` block with a try-catch that just returns instead of letting the exception propagate. – Marko Topolnik May 22 '18 at 10:05
  • True. In fact, I originally was throwing an exception (and letting it propagate beyond the `buildSequence`). This came up when refactoring the exception into an empty sequence. Coming from the .Net world, I've had "Don't use exceptions for regular control flow" drilled into me. Maybe it's more acceptable on the JVM... – Mashmagar May 22 '18 at 13:41
  • The same caveat applies on the JVM, but long-range signaling across the call stack is what exceptions are made for. It would be abuse if you threw and caught the exception within the same method body. – Marko Topolnik May 22 '18 at 13:45

2 Answers2

2

Just use return@buildSequence, which is a labeled return from lambda, while an unlabeled return would mean 'return from the function foo'.

See also: Whats does “return@” mean?

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • IntelliJ continues to report `'return' is not allowed here` during build when I try `return@buildSequence`. – Mashmagar May 21 '18 at 19:45
  • That's odd, I can build and run this code with a return inside `buildSequence`. Java 8 target, Kotlin 1.2.41. – zsmb13 May 21 '18 at 19:49
  • Indeed, here's a runnable sample: [(sample)](https://try.kotlinlang.org/#/UserProjects/nvriuot7vnd4u4bu94phn51okm/i4qh42accgqvlbianr2i69atav) It should work fine. Please try to rebuild the project. If it persists, and you can reproduce it, please report it to [kotl.in/issue](https://kotl.in/issue). – hotkey May 21 '18 at 19:52
  • I figured it out and noted the result in an edit. I dramatically oversimplified my code in the original question, throwing everyone off. Thanks for your help. – Mashmagar May 21 '18 at 19:58
1

Since Kotlin v 1.3.x preferred sequence syntax changed. (buildSequence is replaced by kotlin.sequences.sequence)

Updated "early return from generator" code snippet (includes try-catch and == null early return examples) for post 1.3.x Kotlin:

// gen@ is just a subjective name i gave to the code block.
// could be `anything@` you want
// Use of named returns prevents "'return' is not allowed here" errors.
private fun getItems() = sequence<Item> gen@ {

    val cursor: Cursor?
    try {
        cursor = contentResolver.query(uri,*args)
    } catch (e: SecurityException) {
        Log.w(APP_NAME, "Permission is not granted.")
        return@gen
    }

    if (cursor == null) {
        Log.w(APP_NAME, "Query returned nothing.")
        return@gen
    }

    // `.use` auto-closes Closeable. recommend.
    // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html 
    cursor.use {
        // iterate over cursor to step through the yielded records
        while (cursor.moveToNext()) {
            yield(Item.Factory.fromCursor(cursor))
        }
    }
}

(Thx for all the prior posts that helped me get on "named return" track.)

ddotsenko
  • 4,926
  • 25
  • 24