1
votes

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?

1
Does you read kotlinlang.org/docs/reference/lambdas.html ? In second case latest expression in map block (lambda) is return value. In first case you have so called "non-local return" kotlinlang.org/docs/reference/… - Ruslan
I am unable to refactor your code without specifying all the types and functions. How about publishing a link to you code base? - voddan
Unfortunately no, I can not share the code base. I'll try to write up the relevant code in a self-contained class when I have time. - Saad Farooq

1 Answers

2
votes

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.