Kotlin’s return

Recently I tried out Kotlin and was a bit surprised about its return expression. If you have a Java background like me this might hit you, too.

Look at the following function which does not work as intended:

fun sum(numbers: List<BigDecimal>): BigDecimal {
    return numbers.fold(BigDecimal.ZERO, { acc, value ->
        return acc + value
    })
}

The fold function accepts an initial start value and feeds this and an element of the list into the lambda. The result of the lambda is the new initial value and fold iterates through the whole list until no more elements are left. This example function clearly wants to sum all elements of the list and return the result.

Now, let’s testdrive it with a couple of calls.

println(sum(listOf()))
println(sum(listOf(BigDecimal(3))))
println(sum(listOf(BigDecimal(3), BigDecimal(7))))
println(sum(listOf(BigDecimal(9), BigDecimal(7))))

The output is:

0
3
3
9

It seems ok at first but the last two calls got wrong results. The outcome hints that the function did not iterate through the whole list. Instead it stopped after the first element.
Have another look at the function and you see there are two different return expressions. The most inner return is inside a lambda and on first call the return ends the enclosing function not just the lambda. From the Kotlin documentation:

– return. By default returns from the nearest enclosing function or anonymous function.

So, the intention of returning the result lead us to use return but this ended the whole function prematurely.
How to repair this? Just remove the inner return as lambdas return the last expression anyway:

fun sum(numbers: List<BigDecimal>): BigDecimal {
    return numbers.fold(BigDecimal.ZERO, { acc, value ->
        acc + value
    })
}

Now it works as intended. There is more than one way to patch it up. Sometimes you really want to jump out on the spot and you can.
Kotlin allows a qualified return. Put a label at the lambda and tell the return about it:

fun sum(numbers: List<BigDecimal>): BigDecimal {
    return numbers.fold(BigDecimal.ZERO , accumulator@ { acc, value ->
        return@accumulator acc + value
    })
}

Most often you even do not need to label it yourself but can use an implicit label:

fun sum(numbers: List<BigDecimal>): BigDecimal {
    return numbers.fold(BigDecimal.ZERO , { acc, value ->
        return@fold acc + value
    })
}

Leave a Reply

Your email address will not be published. Required fields are marked *