Kotlin: Multiple returns inside a Lambda - kotlin

I have a function that catches recoverable exceptions and returns a fallback
private fun <T> safely(block: () -> T, fallback: T): T {
return try {
block()
} catch(exc: SomeException) {
// Log the exception/do some logic
fallback
}
}
I want to be able to add this to the public methods of my class e.g.
fun doSomething(): List<String> = safely({
val list = mutableListOf<String>("Hello")
fun someCheck1() = false // Some boolean value
fun someCheck2() = true // Some boolean value
do {
if(someCheck2()) {
return arrayListOf<String>("Hello", "World")
}
} while (someCheck1())
return list
}, arrayListOf<String>())
However I get compiler errors 'return' is not allowed here
Yet if I remove the return then my return in the loop no longer works and it gets highlighted in my IDE with warning the expression is never used
How can I maintain this type of return logic within a Lambda?
Playground Example

Try
fun doSomething(): List<String> = safely(
{
val list = mutableListOf<String>("Hello")
fun someCheck1() = false // Some boolean value
fun someCheck2() = true // Some boolean value
do {
if (someCheck2()) {
return#safely arrayListOf<String>("Hello", "World")
}
} while (someCheck1())
list
}
, arrayListOf<String>())
For further reference, check Using return inside a lambda?
Or you can also extract your block into a separate function (i.e. someCheckFunction(): List<String>), and have fun doSomething() = safely({ someCheckFunction() }, arrayListOf()), but I guess you want to maintain lambda code like above.

return arrayListOf<String>("Hello", "World") here tries to return a value from doSomething function rather than from the lambda passed to safely. However, such return is non-local, since it tries to exit from the function that is not on the top of stack, and therefore it is prohibited.
Another option here is to make safely function inline:
inline fun <T> safely(block: () -> T, fallback: T): T { ... }
and then you'll be able to make a non-local return from block lambda function passed to it.

Related

A 'return' expression required in a function with a block body despite inlined lambda which has the return

I'm trying to create an inlined try-catch helper function but running into a compilation error due to the return statement occurring within the inlined lambda. Below is some code demonstrating the same issue
fun isStringEmpty(myString: String): Boolean {
stringOpHelper {
return myString.length == 0
}
}
inline fun <T> stringOpHelper(fn: () -> T) {
println("performing string operation")
fn()
}
This will compile with the desired effect if a return is added after the inlined function call or an exception is thrown, but that code should be unreachable. Eg:
fun isStringEmpty(myString: String): Boolean {
stringOpHelper {
return myString.length == 0
}
TODO("this is actually unreachable")
}
inline fun <T> stringOpHelper(fn: () -> T) {
println("performing string operation")
fn()
}
My expectation was that the compiler would see that stringOpHelper always calls fn() and the fn in isStringEmpty always returns, so the inlined stringOpHelper call always returns.
Is it possible to define the inline helper function in a way that avoids the need for the unreachable exception / return in the calling function? Otherwise, what's the reason for why this isn't possible?
There is mechanism for such purposes called contracts, but this feature is experimental and its usage must be marked with #ExperimentalContracts or #OptIn(ExperimentalContracts::class)
#OptIn(ExperimentalContracts::class)
inline fun <T> stringOpHelper(fn: () -> T) {
contract {
callsInPlace(fn, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
println("performing string operation")
fn()
}

Single-function listeners using lambda

With all the well-known single-function listeners we can use a simpler lambda notation
view.setOnClickListener { do() }
instead of the original, longer Java way of
view.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
do()
}
})
But what exactly makes this work? I tried to do the same with my own listener:
private var listener: OnCopyPasteClickListener? = null
interface OnCopyPasteClickListener {
fun onPasteClick(text: String)
}
fun setOnCopyPasteClickListener(onCopyPasteClickListener: OnCopyPasteClickListener) {
listener = onCopyPasteClickListener
}
and while the long approach works just fine:
copypaste.setOnCopyPasteClickListener(object : CopyPasteMenu.OnCopyPasteClickListener {
override fun onPasteClick(text: String) {
do(text)
}
})
I can't make it accept the short one:
copypaste.setOnCopyPasteClickListener {
do(it)
}
The IDE gives a type mismatch error.
Actually, if you have only one function to be invoked, I recommend you use Kotlin Callback.
typealias OnDoWorkListener = ((String) -> Unit)
class Work {
var doWork: OnDoWorkListener? = null
fun doSomething() {
doWork?.invoke("Message Here")
}
}
And in your function, you just set the callback to it
fun main() {
val work = Work()
work.doWork = {
Log.d("WORK", "This gets called from the `work` object. Message: $it")
}
work.doSomething();
}
We can also use function to set the listener as well.
class Work {
var doWork: OnDoWorkListener? = null
fun doSomething() {
doWork?.invoke("Message Here")
}
fun setOnWorkListener(listener: OnDoWorkListener) {
doWork = listener
}
}
fun main() {
val work = Work()
work.setOnWorkListener {
Log.d("WORK", "This gets called from the `work` object. Message: $it")
}
work.doSomething()
}
Higher order functions make this work:
Kotlin functions are first-class, which means that they can be stored
in variables and data structures, passed as arguments to and returned
from other higher-order functions. You can operate with functions in
any way that is possible for other non-function values.
From the same page:
Passing a lambda to the last parameter
In Kotlin, there is a convention that if the last parameter of a
function accepts a function, a lambda expression that is passed as the
corresponding argument can be placed outside the parentheses:
val product = items.fold(1) { acc, e -> acc * e }
If the lambda is the only argument to that call, the parentheses can
be omitted entirely:
run { println("...") }
Knowing this, a possible update on your class would look like:
class CopyPaste {
private var listener: (String) -> Unit = {}
fun setOnCopyPasteClickListener(onCopyPasteClickListener: (String) -> Unit) {
listener = onCopyPasteClickListener
}
fun doCopyPaste(value: String) {
listener.invoke(value)
}
}
fun main() {
val copyPaste = CopyPaste()
copyPaste.setOnCopyPasteClickListener { println(it) }
copyPaste.doCopyPaste("ClipboardContent!")
}
The class CopyPaste stores the listener, which is a function that takes a String parameter and does not return anything. Its function setOnCopyPasteClickListener accepts a function with the same signature as the listener property and at the end doCopyPaste accepts a String parameter and passes it to the stored function.
Actually, just after I posted, I searched for more thoughts and found this thread: https://youtrack.jetbrains.com/issue/KT-7770 This is indeed a debated limitation as it currently only applies to Java, not Kotlin itself. There is also a suggestion there that gives almost the required simplicity:
interface OnCopyPasteClickListener {
fun onPasteClick(text: String)
companion object {
inline operator fun invoke(crossinline op: (text: String) -> Unit) =
object : OnCopyPasteClickListener {
override fun onPasteClick(text: String) = op(text)
}
}
}
and then, thanks to this overloaded operator, it can be called as:
copypaste.setOnCopyPasteClickListener(CopyPasteMenu.OnCopyPasteClickListener { text ->
do(text)
})
But as the suggested answers offer a more idiomatic solution, I'll accept one of those, I only wanted to include this approach here for reference.

kotlin, where the return from inside the let go

#kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
and have a function like:
fun getType() : String? {
val type = mContent.let {
if (!TextUtils.isEmpty(it) && it == "TYPE_1") {
return "TYPE_A" . //where it returns to, as the result of the let{}, or as return value to exit the fun getType()?
}
else {
return it
}
}
if (type == "TYPE_A") {
return getType_A()
}
return type
}
where does the return inside the block in the let go{}, exit the fun getType() or just return from the let{}?
The rule in Kotlin is that a plain return returns from the nearest fun.
See the docs here.
If there's an enclosing lambda, then that's only possible if the lambda is inline (i.e. passed to a function marked with the inline keyword); otherwise the compiler will complain.
You can change that if needed by qualifying the return with an enclosing label (e.g. return#myLabel) or function name (e.g. return#let).  But if it's not qualified, you just need to look for the nearest enclosing function defined with fun.

Kotlin closure to solve logical and operation

i want to create a closure where it take all the Boolean expression and methods, gives the final result
something like this
myAnd{
23<34
false
someFunction() //returns true
}
so the answer will be false
The solution i came with is this
fun myAnd(vararg flags: Boolean) = flags.all { it }
myAnd(true , false ,someFunction())
but this solution won't give the power of short circuiting while using and operator
Using blocks to implement short-circuiting and
fun someFunction() = true
fun and(vararg parameters: ()->Boolean): Boolean {
return parameters.all { it() }
}
fun main(args: Array<String>) {
and (
{23<34},
{false},
::someFunction
)
}
This works by passing in each predicate as a block, which means that they can be evaluated one by one, and if any of them returns false then all will return false immediately, short-circuiting the rest of them.
I can think of a quick solution that enables the following use:
val boolResult: Boolean = myAnd {
+ (23 < 34)
+ false
+ someFunction() //returns true
}
Here's the relevant code:
fun myAnd(bb: BooleanBuilder.() -> Unit) = BooleanBuilder().apply { bb() }.result
class BooleanBuilder {
var bools = listOf<Boolean>()
operator fun Boolean.unaryPlus() {
bools += this
}
fun result() = bools.all { it }
}
Explanation: It's a lightweight example of a custom DSL. The interesting part of this is BooleanBuilder.() -> Unit, a function literal with receiver, so that I can pass a lambda that's in the scope of a BooleanBuilder (lambda's receiver). This is necessary to use the member extension Boolean.unaryPlus that again enables the use of + boolean as shown.

Kotlin with-statement as an expression

We can do
val obj = Obj()
with (obj) {
objMethod1()
objMethod2()
}
But is there a way to do this?
val obj = with(Obj()) {
objMethod1()
objMethod2()
}
To solve a common case where you create an object and call a few methods on it to initialise its state.
Sure, you can use the .apply { } stdlib function, which
Calls the specified function block with this value as its receiver and returns this value.
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Usage example:
val obj = Obj().apply {
objMethod1()
objMethod2()
}
You can find it among many other Kotlin idioms here in the reference.
Your second example works too - just make sure that the lambda returns the correct value (the result of the last expression is the returned value of the with expression):
val obj = with(Obj()) {
objMethod1()
objMethod2()
this // return 'this' because we want to assign the new instance to obj
}