Qualified return from lambda

Consider the following (android) code snippet, and more specifically the qualified return from the lambda

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<Button>(R.id.done_button).setOnClickListener { done_btn ->
            val nn_edit = findViewById<EditText>(R.id.nickname_edit)
            if (nn_edit.text.isBlank())
                return@setOnClickListener

            // do something meaningful if edit content is not blank
        }
    }

I just (briefly) read this https://kotlinlang.org/docs/reference/returns.html
And either I’ve missed something, or I have an objection to this construct in general.
You see, it makes sense in the forEach examples in the above docs.
Because in that case the forEach not only lexically encloses the lambda, but also executionally encloses it, i.e. while the lambda executes (possibly multiple times) there is an active forEach invocation down the stack, so it makes sense to return to it.

But in the above example with setOnClickListener { ... } this is not the case – the setOnClickListener only lexically encloses the lambda so where the heck the lambda is returning to (at execution time, I mean) ?

Sorry, what does the objection consist of exactly?

In case you missed that, you don’t have to specify a label to return to, return will just return from the innermost lambda you’re in, in this case return is equivalent to return@setOnClickListener.

You really have a choice of where to return to in case of inline functions, and setOnClickListener come from Java, hence it’s not inline

1 Like

Negative, it is not.
In this case return results in
'return' is not allowed here (compiler error)

The doc says (quote):


As you pointed out setOnClickListener comes from Java hence the return in this case must be qualified.
My problem is return@setOnClickListener somehow suggests to me (as a human being or as a programmer - you name it) that as a result of this return, execution will be passed back to setOnClickListener but it won’t.

I’d be cool with using a non-implicit return label here:

findViewById<Button>(R.id.done_button).setOnClickListener clickHandler@{ done_btn ->
            val nn_edit = findViewById<EditText>(R.id.nickname_edit)
            if (nn_edit.text.isBlank())
                return@clickHandler

To me this is a language usability/UX issue.

Another usability issue is - at the call site, how am I supposed to make a distinction between an inline and non inline function? That is, without using a capable IDE that will show me the declaration of a function when I do Ctrl+hover it? After all, when doing code reviews in Github that sweet on-the-fly inspection is not available.

I take your point but it may just be something you have to get used to. When learning a new language I find there are some things which annoy me for ages, and some I come to live with. This one never confused me, though.

You can’t, but the code won’t compile if you do the wrong thing, and a first step before a code review should be, “does it even compile?”.

There was another thread recently about someone getting confused by an unlabeled return, thinking it would return from the innermost inline function call. But again, I never found that confusing: an unlabeled return always returns from the innermost function definition.

1 Like

whoops sorry :frowning:

I think that Kotlin provide something similar to your need : local functions.

We could rewrite your example with something like this :

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        fun handleClick(target : View) {
            val nn_edit = findViewById<EditText>(R.id.nickname_edit)
            if (nn_edit.text.isBlank())
                return

            // do something meaningful if edit content is not blank
        }

        findViewById<Button>(R.id.done_button).setOnClickListener {done_btn -> handleClick(btn) }
    }

Personally, I’d rather create a member function, but local ones can help when both local variable access and clear logic separation is needed.

Here is a trivial (and totally useless) example of local function that you can execute in a REPL :slightly_smiling_face:

fun printDivision(numerator : Int, denominator : Int) {
    
    fun divideSafely() { 
        if (denominator == 0) {
            println("Cowardly refusing to divide by zero")
            return
        }
        
        println("$numerator / $denominator = ${numerator / denominator}")
    } 
    
    divideSafely()
}

printDivision(2, 0)
println(" -- ")
printDivision(4, 2)

Hope it helps,