How does the Kotlin compiler decide whether an expression enclosed in {
}
is a block or a lambda?
Consider this:
val a: Int
if (cond)
a = 1
else
a = 2
can be written more succinctly as:
val a =
if (cond)
1
else
2
Similarly, one might think that this:
val a: () -> Int
if (cond)
a = { 1 }
else
a = { 2 }
should be able to be written more succinctly like this:
val a =
if (cond)
{ 1 }
else
{ 2 }
But this is not the same: a
is now an Int
, and not of type () -> Int
, because now the { 1 }
is no longer a lambda. What are the rules that say whether something is a lambda or a block?
CodePudding user response:
I didn't look into the Kotlin lexer, but I guess there are few possible places in the code where the compiler expects either a single expression or a block of code. That includes the code immediately following most of control flow statements like: if
, else
, while
, when
(one of its cases), etc. If you put {
in one of these places, it will be interpreted as a start of the block of code related to this control flow statement and not as a lambda.
This is as simple as that. Note that even if you hint the compiler about the type, it still won't work:
// compile error
val a: () -> Int = if (cond)
{ 1 }
else
{ 2 }
It will be interpreted more like this:
// compile error
val a: () -> Int = if (cond) {
1
} else {
2
}
{
after if
condition is always interpreted as a start of block of code. You need to put double {
, }
in cases like this:
// works fine
val a: () -> Int = if (cond) {
{ 1 }
} else {
{ 2 }
}
CodePudding user response:
To put it very succinctly and easy to remember, the first opening brace after an if/when/else/for is always assumed to be the opening of a block. Use double braces if you want a lambda in there.