https://kotlinlang.org logo
Title
d

David Kubecka

03/09/2023, 2:15 PM
Is there any way how to perform a non-local return from a lambda passed to a 3rd party function? E.g.
TransactionTemplate(this).execute { return }
If not what are my options? 🙂 Also can anyone explain why that's not technically possible?
s

Sam

03/09/2023, 2:19 PM
Non-local returns are only possible in inline lambdas, where the compiler know exactly when and where the lambda body is executed
From the compiler’s point of view, a non-inline lambda could end up being executed anywhere, even in a different call stack
As far as what options you have, it depends on what exactly you’re trying to achieve. Throwing and catching a custom exception, while somewhat gross, might be the closest you can get to the desired behaviour.
e.g.
try {
    TransactionTemplate(this).execute { throw MySpecialException() }
} catch (e: MySpecialException) {
    return
}
I say “gross” because it has some performance impact and it can make the control flow hard to follow. I don’t normally like using exceptions for control flow.
j

Joffrey

03/09/2023, 2:31 PM
@Sam's solution indeed works as a general way to deal with this if you know the lambda is called in-place (because, as he mentioned too, that's not necessarily the case). However, there are probably better ways depending on the exact function you're using, how it's calling the lambda, and what you're putting in that lambda. In this specific case, the "special exception" approach will most likely NOT be what you want, because any exception in this lambda would imply rolling back the transaction (as per the documentation of
execute()
) One possibly much simpler way would be to use a local return in the lambda. Could you please provide a more realistic code and explain what you're trying to do with it here?
d

David Kubecka

03/09/2023, 2:43 PM
Yeah, I can imagine that in concrete cases one can always come up with a tailor-made solution/workaround, as did I. My case is basically: • in transaction (lambda) ◦ collect some objects ◦ if there are any objects do something else (in txn) ◦ otherwise stop the processing (return) ◦ return (locallly) the objects • outside transaction ◦ do something with collected objects So I've simply repeated the isObjectsEmpty check outside of the transaction and returned from there.
e

ephemient

03/09/2023, 3:33 PM
exceptions for control flow can also fail if it's not running in the same stack or not rethrown as-is
(Scala used to magically translate non-local returns into
throw scala.runtime.NonLocalReturnException
but that's definitely not something Kotlin should emulate)
d

David Kubecka

03/09/2023, 4:16 PM
Btw what are typical scenarios when the lambda runs on a different stack? I can think of threading. Anything else?
s

Sam

03/09/2023, 4:27 PM
👍 asynchronous callbacks on a separate thread (or coroutine) are the typical example. You could also have a callback that’s executed higher up the current call stack, e.g. if you have a function that creates and returns a lambda.
d

deive

03/09/2023, 4:45 PM
If you know that what you run in execute never returns a null, then why not use a
!!
?
e

ephemient

03/09/2023, 4:48 PM
(I got two threads mixed up)
d

deive

03/09/2023, 5:02 PM
I think the mixup was mine 😳, sorry!