If operator in function expression

Hello guys,
I have bumped into interesting language feature that I cannot make sense of. In Kotlin we can use expression as function body, e.g.
fun foo(x: Int, y: Int) = x + y
I tried today write following function:
fun log() = if (isError()) logError()
surprisingly this function couldn’t be compiled. The reason is following:
Error:(128, 39) ‘if’ must have both main and ‘else’ branches if used as an expression
Can anyone explain me why if listed above is not legit?

Because the if in this case has a return value and the compiler doesn’t know what to return if the statement is false

The compiler could infer Unit as lowest common denominator, but that would hide an error in cases where you actually wanted if as an expression (and not as a statement as in your case). The correct usage would be “classic” if:

fun log(){
    if (isError()) {
        logError()
    }
}

I understood that brackets fix compiler complains but still didn’t understand why ;=) Could elaborate on this statement : “but that would hide an error in cases where you actually wanted if as an expression” ? I just don’t really understand it.

Actually it doesn’t. logError() is void function there is no return value.

In Kotlin there is no void function. Every function, that seems to return nothing returns the Unit object. I guess in that case the compiler maybe should infer Unit as the lowest common denominator but it doesn’t.

If you write fun foo() = ... the compiler basically changes this to

fun foo() {
    return ...
}

Because of your if expression the code becomes

fun foo() {
    return if(isError()) logError()
}

This is a problem as the compiler does not know what to return in the non error case. As you said logError() does not return anything, but that is not true, because as I said above every function returns at least Unit

void returns Unit and Unit is an object, so it does return a value.

Just for the fun of it, write else Unit after it.

The only case in which it does return nothing, is if it returns Nothing :wink:.
This is for the cases that the code after the function cannot be reached, eg. The function always throws an exception; the function has an infinite loop; the function stops the program.

1 Like

Errors can be avoided by this compiler check, because the compiler reminds you of the else branch in cases where it is actually needed.

The following example is useless (and wouldn’t compile) without an else branch:

fun calculate(x: Int): Int = if (x < 2) 0

Thanks a lot for explanation everyone. If I understood correctly compiler can (in theory) actually understand that we want Unit as return type. One last question is this a feature or bug?

I would say it’s a feature.
Using if as assignment should be exhaustive, just like using when should.

2 Likes