Reputation: 2649
It started to appear for me a very weird compiler error in Kotlin
So I have reduced my code to the simplest form. The code doesn't make sense, but it is much easier to try to understand where the error comes from.
I've posted my code in Kotlin Playground. So I don't depend on any local configuration of my computer and the error can be reproducible instantly anywhere for anyone.
My complete code
data class Pilha(
var funs: ArrayList<Int>
= arrayListOf<Int>()
)
var AP: Array<Pilha> = Array<Pilha>(5) { Pilha() }
fun main() {
var ele: String=""
var cl: String = ""
when (ele) {
"soul" -> {
with(AP[0]) {
when {
(cl == "%") -> {
if (funs[0]==1) // <= error in this line
cl = "a"
}
cl == "xU" -> {
// If I comment this line, the error disappears
funs.add(2)
}
else -> { cl="b" } // else of inner when
} // when
} // with
} // "soul"
else->{ cl="c"} // else of outer when
} // when
println("ok")
} // main
The error message
if must have both main and 'else' branches if used as an expression
This error gives in this line
if (funs[0]==1)
If I comment this line, the error disappears
funs.add(2)
it is a big mistery because it's obvious that is a valid if
flow control statement. I'm not using if
as an expression.
Obviously putting a dead else
(else {}
) on this if
also solve the error, but don't explain it.
Kotlin Playground:
Update: As Pawel has pointed out to me, it's a question of type return conflict, linked to with
statement. As I am a classic Pascal programmer, I was using with
, how Pascal users did it.
For this purpose, it is best, like Pawel said, using object.apply {}
. So one can, as in the case of with
, use the properties and methods of the object directly without the accessor dot inside brackets ({}).
Upvotes: 1
Views: 1371
Reputation: 17248
You're using with(AP[0]) { ... }
function with lambda signature of T.() -> R
. It REQUIRES a return value from within the lambda, and last statement inside it is when
block so it is used as an expression to determine return value.
Error is caused because in case of cl == %
and funs[0]!=1
it's not possible to determine value to return.
Fix it by replacing your with
with a scoping function that does not need return value, for example AP[0].apply { ... }
with signature of T.() -> Unit
.
Edit: why commenting out funs.add(2)
"fixes" the error?
It's because that call returns Boolean
value which changes signature of entire when
block:
// this when block can be used as an expression because it returns value of type Any
when {
(cl == "%") -> if (funs[0]==1) cl = "a" // if true returns Unit, else undefined
cl == "xU" -> { funs.add(2) } // returns Boolean
else -> cl="b" // does not return value (Unit)
}
Now when you comment out funs.add(2)
or change second case to {funs.add(2) ; Unit }
this makes return type of entire when
block a Unit
, which means it's no longer used as an expression and returning value is not required.
// this when block cannot be used as an expression because it always returns Unit (void)
when {
(cl == "%") -> if (funs[0]==1) cl = "a" // does not return value (Unit)
cl == "xU" -> { funs.add(2); Unit } // ignore add return value by returning Unit
else -> cl="b" // does not return value (Unit)
}
Upvotes: 2