Simplifying return from multiple nested blocks in Kotlin - kotlin

The code in question is as:
fun get(context: Context, s: String): MyObjectDb? {
return context.database.use {
return#use select(MyObjectDb.TABLE_NAME, *MyObjectDb.PROJECTION)
.whereArgs("${MyObjectDb.COLUMN_S} = {s}", "s" to s)
.exec {
return#exec getOne(MyObjectDb::fromCursor)
}
}
}
When I check this for code style (sonar with Kotlin plugin that uses detekt) I get a warning that I should "Restrict the number of return statements in methods."
Is there a way to only return in return#exec or to write the code in more Kotlinized way - without so many returns.

You can omit return when a lambda only contain a single expression. Since your function also only contains a single expression, you can write the function body after a = to omit the return here too. You can therefore shorten your code to this:
fun get(context: Context, s: String): MyObjectDb? = context.database.use {
select(MyObjectDb.TABLE_NAME, *MyObjectDb.PROJECTION)
.whereArgs("${MyObjectDb.COLUMN_S} = {s}", "s" to s)
.exec { getOne(MyObjectDb::fromCursor) }
}

Related

Returns and qualified labels in Kotlin's Lambda functions

I am struggling to understand a concept in kotlin.
Looking at this
val lambda = { greeting: String, name: String ->
if(greeting.length < 3) return // error: return not allowed here
println("$greeting $name")
}
This would result in an error. Rewriting the lambda expression to this
val lambda = greet# { greeting: String, name: String ->
if(greeting.length < 3) return#greet
println("$greeting $name")
}
It works. And it works because "... return from our lambda to the outer, calling function..."
What I am struggling to understand is, what the outer calling function is. There is no other function, calling lambda. Whats going on under the hood here?
You're right that there is no other function calling lambda right now. What it means is that, when you do decide to call lambda, it will return to the function where you are doing that.
For example, if you have:
fun someFunction() {
lambda()
// some other code...
}
Then that return#greet will return to someFunction. someFunction is the "outer, calling function".
Just saying that might not be very meaningful, until you compare this to return without a label. Let's make up a situation where return and return#label are both allowed:
fun main() {
aMoreMeaningfulExample foo#{
if (it == 3) return#foo
println(it)
}
}
inline fun aMoreMeaningfulExample(lambda: (Int) -> Unit) {
for (x in 1..5) {
lambda(x)
}
println("Done!")
}
Here, return#foo returns to the "outer, calling function" of the lambda, which is aMoreMeaningfulExample. This means that println(it) will be skipped when it is 3. After returning to aMoreMeaningfulExample, the for loop there will still continue as normal, so you will see the rest of the numbers, and "Done!" being printed.
On the other hand, if you used return, it would return to whatever called main, in other words, terminating the program. You will only see "1" and "2" being printed.

Can I return from lambda by invoking some function inside its body (non-local returns)

So I'm making regexes for collections (all quantifiers are possessive). It looks like this (keep in mind the example is overly simplified for readability's sake):
val mayBeAPerson: Boolean = "totally not a person"
.toList()
.matches { // this: PatternScope
one { it.isUpperCase() } // execution of lambda could end after this method
moreThan(0) { it.isLetter() }
one { it == ' ' }
lessThan(2) { // this: PatternScope
one { it.isUpperCase() }
one { it == '.' }
one { it == ' ' }
}
one { it.isUpperCase() }
moreThan(0) { it.isLetter() }
}
As you can see, execution of lambda passed to matches could end after first one, as the predicate passed to it doesn't match first character in List. And it indeed does end. However, my solution is the opposite of elegant, as it uses throwing an exception in one and catching it in matches.
fun List<Char>.matches(build: PatternScope.() -> Unit) = try {
val scope = PatternScope(iterator())
scope.build() // may throw MatchFailed
!scope.iterator.hasNext()
} catch (_: MatchFailed) {
false
}
class PatternScope(private val iterator: Iterator<Char>) {
inline fun one(predicate: (element: Char) -> Boolean) {
if (!iterator.hasNext() || !predicate(iterator.next())) {
throw MatchFailed("match failed")
}
}
.
. etc
.
}
It totally works, but I can't help but wonder: is there a better way? I do know throwing exceptions like this is just a fancy GOTO, and I could wrap all the methods of PatternScope in ifs, like this:
class PatternScope(private val iterator: Iterator<Char>) {
private var matchFailed = false
inline fun one(predicate: (element: Char) -> Boolean) {
if (!matchFailed) {
if (!iterator.hasNext() || !predicate(iterator.next())) {
matchFailed = true
}
}
}
inline fun moreThan(n: Int, predicate: (element: Char) -> Boolean) {
if (!matchFailed) {
// logic
}
}
.
. etc
.
}
Is it more elegant though? Now I'm invoking all the functions in lambda passed to matches, and I like it even less to be honest.
My real question is: is there even better way to do it? Some magic solution to return from lambda I don't even have real access to? Some non-local returns, but from functions lambda hasn't even see yet?
Can I return from lambda by invoking some function inside its body?
Edit
Just to clarify, let's say we have a lambda:
val lambda: () -> Unit = {
someMethod() // this should return from lambda (in some cases)
someOtherMethod() // this shouldn't be invoked
}
How should the body of someMethod look like, so that someOtherMethod does not even execute when the lambda is invoked? Is there any other way but making someMethod throw an exception and wrapping lambda in try-catch block like this:
try {
lambda() // throws
} catch (_: SomeThrowableIdk) { }
I don't see a better way, but please prove me wrong.
I assume you're actually using #PublishedApi since you have a private iterator and public inline functions that access it.
Since Kotlin doesn't have checked exceptions, it is against Kotlin convention to throw Exceptions for things that are not actually errors in the program (bugs). Your first approach feels a little hacky for this reason. Since your API has public inline functions, there's no way to totally encapsulate the exceptions. You could switch to non-inline functions and storing the steps in a collection to be run internally, but this is surely more runtime overhead than the inline functions or your second approach with if statements.
Your second approach is more like a typical builder, so I don't see the problem with it. Since your functions are inline, it's not like your compiled code has a bunch of unnecessary function calls. Just if statements. You could however add a helper function to clean up your code at all the sub-functions, though I'm not sure if this can extend to the complexity of your actual class:
class PatternScope(#PublishedApi internal val iterator: Iterator<Char>) {
#PublishedApi internal var matchFailed = false
#PublishedApi internal inline fun nextRequire(require: () -> Boolean) {
matchFailed = matchFailed || !require()
}
inline fun one(predicate: (element: Char) -> Boolean) = nextRequire {
iterator.hasNext() && predicate(iterator.next())
}
}
There's no way to do what you described in your edit. Non-local returns work only with lambdas. To support something like what you describe, Kotlin would need syntax for a special kind of function that has the ability to return from the function that calls it. A function like this would have to have a new kind of signature that also declares the return type of the type of function that is permitted to call it. There simply is no such syntax or function type like that in Kotlin.

Kotlin: Multiple returns inside a Lambda

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.

Collect iterables into one variable (list)

Consider this:
fun readFiles(directory: String): List<File> {
val result = ArrayList<File>()
File(directory).walkTopDown().forEach {
result.addAll(getFiles(it))
}
return result
}
fun getFiles(file: File): List<File> { ... }
How can rewrite this so I don't need to initialize the result ArrayList but can directly return File(directory).walkTopDown().????
The question is not about what's the best way to read files or anything, just how I can write the above code more concise while doing the same.
You can use flatMap for this purpose. It first maps each element to a Sequence using your mapping function (so you kinda get a Sequence<Sequence<File>>), then it flattens every result back to a Sequence<File>.
Since walkTopDown returns a FileTreeWalk (which is a subclass of Sequence<File>), and you return a List<File>, you have to do some conversions as well. You can remove these conversions if you make getFiles and readFiles return a Sequence<File> instead.
fun readFiles(directory: String): List<File> {
return File(directory)
.walkTopDown()
.flatMap { getFiles(it).asSequence() }
.toList()
}

Type inference with functional builders

I'm using Kotlin KBuilders with some protobuffs and have run into a situation that is confusing me.
To start off, I have a function that takes a file name and list of serialized JSON and deserialized that JSON to a protobuff.
fun parseFileData(fileName: String, lines: List<String>): Data.Builder.() -> Unit = when (fileName) {
SOME_FILE_NAME -> deserializeLinesToModel(lines, DataModel::class.java)
.let {
return {
dataMeasurement = buildDataMeasurement {
property1 = it.reduce { acc, n -> acc + n }
measurementMsec = it.map { it.measurementMsec }
}
}
}
else -> throw UnsupportedOperationException()
The first thing I didn't understand was why I needed the return inside the let block. But it worked so I moved on.
I later decided to refactor some stuff to make code elsewhere simpler and ended up with something like this:
fun parseFileData(fileName: String, factory: DataFactory): Sequence<Data.Builder.() -> Unit> = when (fileName) {
SOME_FILE_NAME -> factory.getSomeFileSequence() // returns Sequence<Model>
.batch(1000) // process data in batches of 1000 to reduce memory footprint and payload size
.map { return {
dataMeasurement = buildDataMeasurement {
property1 = it.reduce { acc, n -> acc + n }
measurementMsec = it.map { it.measurementMsec }
}
}
else -> throw UnsupportedOperationException()
So basically, instead of processes each batch as a list, I read the sequence from the factory, batch it into a sequence of lists and try to map each list to a Data.Builder.() -> Unit. However, this time I get return is not allowed here. I've tried multiple variations, with and without return and map and let and whatnot. The closest I've gotten to is a return type of Sequence<() -> Unit> which fails type inference.
Can anyone explain what's going on here? And why this type cannot be inferred?
return in the map lambda is a non-local return. It tries to return from the closest fun function, which happens to be parseFileData.
Non-local returns are only allowed from inline functions, whose lambda parameters are inlined at the call site, and the map extension for Sequence is not an inline function.
If you want to return a value from the lambda itself, use qualified return return#map ..., or omit it completely: then the last expression in the block will be returned as the result.