Is there any way how to perform a non-local return...
# getting-started
d
Is there any way how to perform a non-local return from a lambda passed to a 3rd party function? E.g.
Copy code
TransactionTemplate(this).execute { return }
If not what are my options? šŸ™‚ Also can anyone explain why that's not technically possible?
nope 2
s
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
šŸ‘ 1
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.
Copy code
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
@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
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
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)
šŸ˜² 3
d
Btw what are typical scenarios when the lambda runs on a different stack? I can think of threading. Anything else?
s
šŸ‘ 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
If you know that what you run in execute never returns a null, then why not use a
!!
?
e
(I got two threads mixed up)
d
I think the mixup was mine šŸ˜³, sorry!