returning the last expression in a block - kotlin

I am learning the new and very beautiful language Kotlin and everything seems to be very logical and consistent. I only found one thing which seems like an arbitrary exception to the rule than a solid rule. But maybe I lack enough understanding some deeper reasons behind the rule.
I know that in if-else and when statements there are blocks of code, then the last expression is returned. In the next example 1 or 2 are returned depending on the condition - in our case it returns 1.
val x = if (1 < 2) {println("something"); 1} else {println("something else"); 2}
On the other hand this does not hold for any code block. The next line assigns y not to 1 but to the whole block of code as a lambda.
val y = {println("something"); 1}
Similarly in function body, the last expression is not returned. This does not even compile.
fun z() : Int {
println("something")
1
}
So what exactly is the rule? Is it really so arbitrary like: if in if-else or when statement which is used as expression there is a block of code, then the last expression in the block is returned. Otherwise the last expression is not returned to outer scope. Or am I missing something?

you misunderstand the curly brackets {}, when around with all flow-control statement it is just a block, for example:
if (condition) { //block here
}
WHEN the {} is declared separately it is a lambda expression, for example:
val lambda: () -> Int = { 1 }; // lambda
WHEN you want to return a lambda in if-else expression, you must double the curly brackets {} or using parentheses () to distinguish between the block and the lambda expression or make the {} as lambda explicitly, for example:
val lambda1: () -> Int = if (condition) { { 1 } } else { { 2 } };
val lambda2: () -> Int = if (condition) ({ 1 }) else ({ 2 });
val lambda3: () -> Int = if (condition) { -> 1 } else { -> 2 };
If a function does not return any useful value, its return type is Unit. Unit is a type with only one value - Unit. This value does not have to be returned explicitly.
On the other hand, a common function must have a explicit return statement if its return type if not a Unit:
fun z(): Int { return 1; }
Another case is a function return Nothing, the return statement don't allowed at all, because you can't create a Nothing instance, for example:
fun nothing(): Nothing {
return ?;// a compile error raising
}
WHEN a function has only one expression then you can using single-expression function instead, for example:
fun z() = 1;

There is a difference between a lambda block and just a 'normal' block, in your case the "y" is just a lambda that needs to be executed to get the returned value:
val block: () -> Int = { 5 }
val five: Int = { 5 }()
val anotherFive = block()
So if you want a block that acts as a lambda, you can create a lambda and execute it right away with "()". This way, your "z" function would compile like so:
fun z() : Int = {
println("something")
1
}()
(Which, of course, does not make much sense and is not very efficient)

You are absolutely right, V.K. The choice is completely arbitrary. Well, probably not completely arbitrary but related to parsing complexities arising from curly braces being chosen to denote both blocks and lambdas.
Actually, besides the case you make for block expressions, there also is the situation of (simple) blocks: just group of statements enclosed in braces. For example, Java supports (simple) blocks and you can write:
String toBeComputed ; {
// Relatively long sequence of operations here
toBeComputed= resultingValue ;
}
I use this idiom quite frequently because:
It's not always convenient to extract the code into a function.
Like a function, it delimits the code and documents its nature without introducing additional names or comments.
Sometimes it's preferable to initialize two or more variables in tandem and introducing a result-value class with this specific purpose feels like an overkill.
Also like a function it doesn't pollute the outer namespace with temporary variables only used internally.
Anecdotally, as Java does not support block expressions I would write the example as
int y ; {
println("something");
y= 1 ;
}
Other languages (ALGOL and Scala come to my mind) do support block expressions (and in the case of Scala also lambdas, but with a different syntax). In these languages what you proposed (in Scala syntax)
val y= { println("something"); 1 }
is completely valid and does not require () to force a lambda to evaluate (because there is no lambda!).
In conclusion, Kotlin designers did choose to make the language less consistent than ALGOL and Scala in terms of blocks and block expression, probably in favor of convenience.
I hope my long response shows that what you expected from Kotlin was not illogical, just not the case because of some language design choice. The principle of less surprise did not work this time.

Related

Kotlin lambdas with receivers: seeking clarification on my mental model

I'm trying to build a good mental model for lambdas with receivers in Kotlin, and how DSLs work. The simples ones are easy, but my mental model falls apart for the complex ones.
Part 1
Say we have a function changeVolume that looks like this:
fun changeVolume(operation: Int.() -> Int): Unit {
val volume = 10.operation()
}
The way I would describe this function out loud would be the following:
A function changeVolume takes a lambda that must be applicable to an Int (the receiver). This lambda takes no parameters and must return an Int. The lambda passed to changeVolume will be applied to the Int 10, as per the 10.lambdaPassedToFunction() expression.
I'd then invoke this function using something like the following, and all of a sudden we have the beginning of a small DSL:
changeVolume {
plus(100)
}
changeVolume {
times(2)
}
This makes a lot of sense because the lambda passed is directly applicable to any Int, and our function simply makes use of that internally (say 10.plus(100), or 10.times(2))
Part 2
But take a more complex example:
data class UserConfig(var age: Int = 0, var hasDog: Boolean = true)
val user1: UserConfig = UserConfig()
fun config(lambda: UserConfig.() -> Unit): Unit {
user1.lambda()
}
Here again we have what appears to be a simple function, which I'd be tempted to describe to a friend as "pass it a lambda that can have a UserConfig type as a receiver and it will simply apply that lambda to user1".
But note that we can pass seemingly very strange lambdas to that function, and they will work just fine:
config {
age = 42
hasDog = false
}
The call to config above works fine, and will change both the age and the hasDog properties. Yet it's not a lambda that can be applied the way the function implies it (user1.lambda(), i.e. there is no looping over the 2 lines in the lambda).
The official docs define those lambdas with receivers the following way: "The type A.(B) -> C represents functions that can be called on a receiver object of A with a parameter of B and return a value of C."
I understand that the age and the hasDog can be applied to the user1 individually, as in user1.age = 42, and also that the syntactic sugar allows us to omit the this.age and this.hasDog in the lambda declaration. But how can I reconcile the syntax and the fact that both of those will be run, sequentially nonetheless! Nothing in the function declaration of config() would lead me to believe that events on the user1 will be applied one by one.
Is that just "how it is", and sort of syntactic sugar and I should learn to read them as such (I mean I can see what it's doing, I just don't quite get it from the syntax), or is there more to it, as I imagine, and this all comes together in a beautiful way through some other magic I'm not quite seeing?
The lambda is like any other function. You aren't looping through it. You call it and it runs through its logic sequentially from the first line to a return statement (although a bare return keyword is not allowed). The last expression of the lambda is treated as a return statement. If you had not defined your parameter as receiver, but instead as a standard parameter like this:
fun config(lambda: (UserConfig) -> Unit): Unit {
user1.lambda()
}
Then the equivalent of your above code would be
config { userConfig ->
userConfig.age = 42
userConfig.hasDog = false
}
You can also pass a function written with traditional syntax to this higher order function. Lambdas are only a different syntax for it.
fun changeAgeAndRemoveDog(userConfig: UserConfig): Unit {
userConfig.age = 42
userConfig.hasDog = false
}
config(::changeAgeAndRemoveDog) // equivalent to your lambda code
or
config(
fun (userConfig: UserConfig): Unit {
userConfig.age = 42
userConfig.hasDog = false
}
)
Or going back to your original example Part B, you can put any logic you want in the lambda because it's like any other function. You don't have to do anything with the receiver, or you can do all kinds of stuff with it, and unrelated stuff, too.
config {
age = 42
println(this) // prints the toString of the UserConfig receiver instance
repeat(3) { iteration ->
println(copy(age = iteration * 4)) // prints copies of receiver
}
(1..10).forEach {
println(it)
if (it == 5) {
println("5 is great!")
}
}
hasDog = false
println("I return Unit.")
}

Kotlin fun() vs lambda is there difference?

This question is about fun() vs a lambda block definitions and scopes.
i have tried define the expressions in two ways. Here is what i have tried:
val myFunction = fun(){
println("i am in a function")
}
//but i also tried doing this:
val myFunction = {
println("i am in a lambda")
}
my problem is i do not know if they are equivalent and same thing ?
The differences are best described in https://kotlinlang.org/docs/reference/lambdas.html#anonymous-functions:
Anonymous functions allow you to specify return type, lambdas don't.
If you don't, return type inference works like for normal functions, and not like for lambdas.
As #dyukha said, the meaning of return is different:
A return statement without a label always returns from the function declared with the fun keyword. This means that a return inside a lambda expression will return from the enclosing function, whereas a return inside an anonymous function will return from the anonymous function itself.
There is no implicit it parameter, or destructuring.
Your specific cases will be equivalent, yes.
See the reference: https://kotlinlang.org/docs/reference/lambdas.html
There are several ways to obtain an instance of a function type:
Using a code block within a function literal, in one of the forms:
a lambda expression: { a, b -> a + b },
an anonymous function: fun(s: String): Int { return s.toIntOrNull() ?: 0 }
Both give you a function object which can be used interchangeably

Example of when should we use run, let, apply, also and with on Kotlin

I wish to have a good example for each function run, let, apply, also, with
I have read this article but still lack of an example
All these functions are used for switching the scope of the current function / the variable. They are used to keep things that belong together in one place (mostly initializations).
Here are some examples:
run - returns anything you want and re-scopes the variable it's used on to this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
The password generator is now rescoped as this and we can therefore set seed, hash and hashRepetitions without using a variable.
generate() will return an instance of Password.
apply is similar, but it will return this:
val generator = PasswordGenerator().apply {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
}
val pasword = generator.generate()
That's particularly useful as a replacement for the Builder pattern, and if you want to re-use certain configurations.
let - mostly used to avoid null checks, but can also be used as a replacement for run. The difference is, that this will still be the same as before and you access the re-scoped variable using it:
val fruitBasket = ...
apple?.let {
println("adding a ${it.color} apple!")
fruitBasket.add(it)
}
The code above will add the apple to the basket only if it's not null. Also notice that it is now not optional anymore so you won't run into a NullPointerException here (aka. you don't need to use ?. to access its attributes)
also - use it when you want to use apply, but don't want to shadow this
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().also { apple ->
this.weight += apple.weight
add(apple)
}
...
}
...
fun add(fruit: Fruit) = ...
}
Using apply here would shadow this, so that this.weight would refer to the apple, and not to the fruit basket.
Note: I shamelessly took the examples from my blog
There are a few more articles like here, and here that are worth to take a look.
I think it is down to when you need a shorter, more concise within a few lines, and to avoid branching or conditional statement checking (such as if not null, then do this).
I love this simple chart, so I linked it here. You can see it from this as written by Sebastiano Gottardo.
Please also look at the chart accompanying my explanation below.
Concept
I think it as a role playing way inside your code block when you call those functions + whether you want yourself back (to chain call functions, or set to result variable, etc).
Above is what I think.
Concept Example
Let's see examples for all of them here
1.) myComputer.apply { } means you want to act as a main actor (you want to think that you're computer), and you want yourself back (computer) so you can do
var crashedComputer = myComputer.apply {
// you're the computer, you yourself install the apps
// note: installFancyApps is one of methods of computer
installFancyApps()
}.crash()
Yup, you yourself just install the apps, crash yourself, and saved yourself as reference to allow others to see and do something with it.
2.) myComputer.also {} means you're completely sure you aren't computer, you're outsider that wants to do something with it, and also wants it computer as a returned result.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
3.) with(myComputer) { } means you're main actor (computer), and you don't want yourself as a result back.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
4.) myComputer.run { } means you're main actor (computer), and you don't want yourself as a result back.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
but it's different from with { } in a very subtle sense that you can chain call run { } like the following
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
This is due to run {} is extension function, but with { } is not. So you call run { } and this inside the code block will be reflected to the caller type of object. You can see this for an excellent explanation for the difference between run {} and with {}.
5.) myComputer.let { } means you're outsider that looks at the computer, and want to do something about it without any care for computer instance to be returned back to you again.
myComputer.let {
myGrandpa.installVirusOn(it)
}
The Way to Look At It
I tend to look at also and let as something which is external, outside. Whenever you say these two words, it's like you try to act up on something. let install virus on this computer, and also crash it. So this nails down the part of whether you're an actor or not.
For the result part, it's clearly there. also expresses that it's also another thing, so you still retain the availability of object itself. Thus it returns it as a result.
Everything else associates with this. Additionally run/with clearly doesn't interest in return object-self back. Now you can differentiate all of them.
I think sometimes when we step away from 100% programming/logic-based of examples, then we are in better position to conceptualize things. But that depends right :)
There are 6 different scoping functions:
T.run
T.let
T.apply
T.also
with
run
I prepared a visual note as the below to show the differences :
data class Citizen(var name: String, var age: Int, var residence: String)
Decision depends on your needs. The use cases of different functions overlap, so that you can choose the functions based on the specific conventions used in your project or team.
Although the scope functions are a way of making the code more concise, avoid overusing them: it can decrease your code readability and lead to errors. Avoid nesting scope functions and be careful when chaining them: it's easy to get confused about the current context object and the value of this or it.
Here is another diagram for deciding which one to use from https://medium.com/#elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84
Some conventions are as the following :
Use also for additional actions that don't alter the object, such as logging or printing debug information.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
The common case for apply is the object configuration.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
If you need shadowing, use run
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
If you need to return receiver object itself, use apply or also
let, also, apply, takeIf, takeUnless are extension functions in Kotlin.
To understand these function you have to understand Extension functions and Lambda functions in Kotlin.
Extension Function:
By the use of extension function, we can create a function for a class without inheriting a class.
Kotlin, similar to C# and Gosu, provides the ability to extend a class
with new functionality without having to inherit from the class or use
any type of design pattern such as Decorator. This is done via special
declarations called extensions. Kotlin supports extension functions
and extension properties.
So, to find if only numbers in the String, you can create a method like below without inheriting String class.
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
you can use the above extension function like this,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
which is prints true.
Lambda Functions:
Lambda functions are just like Interface in Java. But in Kotlin, lambda functions can be passed as a parameter in functions.
Example:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
You can see, the block is a lambda function and it is passed as a parameter. You can use the above function like this,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
The above function will print like this,
Block executed
true
I hope, now you got an idea about Extension functions and Lambda functions. Now we can go to Extension functions one by one.
let
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
Two Types T and R used in the above function.
T.let
T could be any object like String class. so you can invoke this function with any objects.
block: (T) -> R
In parameter of let, you can see the above lambda function. Also, the invoking object is passed as a parameter of the function. So you can use the invoking class object inside the function. then it returns the R (another object).
Example:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
In above example let takes String as a parameter of its lambda function and it returns Pair in return.
In the same way, other extension function works.
also
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
extension function also takes the invoking class as a lambda function parameter and returns nothing.
Example:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
apply
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Same as also but the same invoking object passed as the function so you can use the functions and other properties without calling it or parameter name.
Example:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
You can see in the above example the functions of String class directly invoked inside the lambda funtion.
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
Example:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
In above example number will have a string of phoneNumber only it matches the regex. Otherwise, it will be null.
takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
It is the reverse of takeIf.
Example:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
number will have a string of phoneNumber only if not matches the regex. Otherwise, it will be null.
You can view similar answers which is usefull here difference between kotlin also, apply, let, use, takeIf and takeUnless in Kotlin
According to my experience, since such functions are inline syntactic sugar with no performance difference, you should always choose the one that requires writing the least amount of code in the lamda.
To do this, first determine whether you want the lambda to return its result (choose run/let) or the object itself (choose apply/also); then in most cases when the lambda is a single expression, choose the ones with the same block function type as that expression, because when it's a receiver expression, this can be omitted, when it's a parameter expression, it is shorter than this:
val a: Type = ...
fun Type.receiverFunction(...): ReturnType { ... }
a.run/*apply*/ { receiverFunction(...) } // shorter because "this" can be omitted
a.let/*also*/ { it.receiverFunction(...) } // longer
fun parameterFunction(parameter: Type, ...): ReturnType { ... }
a.run/*apply*/ { parameterFunction(this, ...) } // longer
a.let/*also*/ { parameterFunction(it, ...) } // shorter because "it" is shorter than "this"
However, when the lambda consists of a mix of them, it's up to you then to choose the one that fits better into the context or you feel more comfortable with.
Also, use the ones with parameter block function when deconstruction is needed:
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
Here is a brief comparison among all these functions from JetBrains's official Kotlin course on Coursera Kotlin for Java Developers:
I must admit that the difference is not so obvious at first glance, among other things because these 5 functions are often interchangeable. Here is my understanding :
APPLY -> Initialize an object with theses properties and wait for the object
val paint = Paint().apply {
this.style = Paint.Style.FILL
this.color = Color.WHITE
}
LET -> Isolate a piece of code and wait for the result
val result = let {
val b = 3
val c = 2
b + c
}
or
val a = 1
val result = a.let {
val b = 3
val c = 2
it + b + c
}
or
val paint: Paint? = Paint()
paint?.let {
// here, paint is always NOT NULL
// paint is "Paint", not "Paint?"
}
ALSO -> Execute 2 operations at the same time and wait for the result
var a = 1
var b = 3
a = b.also { b = a }
WITH -> Do something with this variable/object and don't wait for a result (chaining NOT allowed )
with(canvas) {
this.draw(x)
this.draw(y)
}
RUN -> Do something with this variable/object and don't wait for a result (chaining allowed)
canvas.run {
this.draw(x)
this.draw(y)
}
or
canvas.run {this.draw(x)}.run {this.draw(x)}

What are the rules of semicolon inference?

Kotlin provides “semicolon inference”: syntactically, subsentences (e.g., statements, declarations etc) are separated by the pseudo-token SEMI, which stands for “semicolon or newline”. In most cases, there’s no need for semicolons in Kotlin code.
This is what the grammar page says. This seems to imply that there is a need to specify semicolons in some cases, but it doesn't specify them, and the grammar tree below doesn't exactly make this obvious. Also I have suspicions that there are some cases where this feature may not work correctly and cause problems.
So the question is when should one insert a semicolon and what are the corner cases one needs to be aware of to avoid writing erroneous code?
You only need to specify semicolons in cases where it is ambiguous to the compiler what you are trying to do, and the absence of a semicolon would result in an obvious compiler error.
The rule is: Don't worry about this and don't use semicolons at all (other than the two cases below). The compiler will tell you when you get it wrong, guaranteed. Even if you accidentally add an extra semicolon the syntax highlighting will show you it is unnecessary with a warning of "redundant semicolon".
The two common cases for semi-colons:
An enum class that has a list of enums and also properties or functions in the enum requires a ; after the enum list, for example:
enum class Things {
ONE, TWO;
fun isOne(): Boolean = this == ONE
}
And in this case the compiler will tell you directly if you fail to do it correctly:
Error:(y, x) Kotlin: Expecting ';' after the last enum entry or '}' to close enum class body
Otherwise the only other common case is when you are doing two statements on the same line, maybe for brevity sake:
myThingMap.forEach { val (key, value) = it; println("mapped $key to $value") }
Absence of a semicolon in this last example will give you a more mysterious error at the point where it is confused what you are doing. It is really hard to make some code that would both be valid as two statements separated by a semicolon that are also valid when the semicolon is removed and they become one.
In the past there were other cases like an initialization block of a class which was more "anonymous" { ... } before Kotlin 1.0 and later became init { ... } which no longer needed the semicolon because it is much clearer. These cases no longer remain in the language.
Confidence in this feature:
Also I have suspicions that there are some cases where this feature may not work correctly and cause problems.
The feature works well, there is no evidence anywhere that there are problems with this feature and years of Kotlin experience have not turned up any known cases where this feature backfires. If there is a problem with a missing ; the compiler will report an error.
Searching all of my open-source Kotlin, and our internal rather large Kotlin projects, I find no semi-colons other than the cases above -- and very very few in total. Supporting the notion of "don't use semicolons in Kotlin" as the rule.
However, it is possible that you can intentionally contrive a case where the compiler doesn't report an error because you created code that is valid and has different meaning with and without a semicolon. This would look like the following (a modified version of the answer by #Ruckus):
fun whatever(msg: String, optionalFun: ()->Unit = {}): () -> Unit = ...
val doStuff: () -> Unit = when(x) {
is String -> {
{ doStuff(x) }
}
else -> {
whatever("message") // absence or presence of semicolon changes behavior
{ doNothing() }
}
}
In this case doStuff is being assigned the result of the call to whatever("message") { doNothing() } which is a function of type ()->Unit; and if you add a semicolon it is being assigned the function { doNothing() } which is also of type ()->Unit. So the code is valid both ways. But I have not seen something like this occur naturally since everything has to line up perfectly. The feature suggested emit keyword or ^ hat operator would have made this case impossible, and it was considered but dropped before 1.0 due to strongly opposed opinions and time constraints.
I addition to Jayson Minard's answer, I've run into one other weird edge case where a semicolon is needed. If you are in a statement block that returns a function without using the return statement, you need a semicolon. For example:
val doStuff: () -> Unit = when(x) {
is String -> {
{ doStuff(x) }
}
else -> {
println("This is the alternate"); // Semicolon needed here
{ doNothing() }
}
}
Without the semicolon, Kotlin thinks the { doNothing() } statement is a second argument to println() and the compiler reports an error.
Kotlin seems to mostly infer semicolons eagerly. There seem to be exceptions (as shown in Jayson Minard's enum example).
Generally, the type system will catch badly inferred semicolons, but here are some cases where the compiler fails.
If an invocation's arguments are in the next line (including the parenthesis), Kotlin will assume that arguments are simply a new parenthesised expression statement:
fun returnFun() : (x: Int) -> Unit {
println("foo")
return { x -> println(x) }
}
fun main(args: Array<String>) {
println("Hello, world!")
returnFun()
(1 + 2) // The returned function is not called.
}
A more common case could be the following, where we have a return with the expression in the next line. Most of the time the type system would complain that there is no return value, but if the return type is Unit, then all bets are off:
fun voidFun() : Unit {
println("void")
}
fun foo() : Unit {
if (1 == 1) return
voidFun() // Not called.
}
fun bar() : Unit {
if (1 == 1)
return
voidFun() // Not called.
}
The bar function could potentially happen, if the return voidFun() wouldn't fit on one line. That said, must developers would simply write the call to the function on a separate line.

Compilation error: Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure

To simplify my real use case, let's suppose that I want to find the maximum number in a list:
var max : Int? = null
listOf(1, 2, 3).forEach {
if (max == null || it > max) {
max = it
}
}
However, compilation fails with the following error:
Smart cast to 'Int' is impossible, because 'max' is a local variable that is captured by a changing closure
Why does a changing closure prevent smart cast from working in this example?
In general, when a mutable variable is captured in a lambda function closure, smart casts are not applicable to that variable, both inside the lambda and in the declaring scope after the lambda was created.
It's because the function may escape from its enclosing scope and may be executed later in a different context, possibly multiple times and possibly in parallel. As an example, consider a hypothetical function List.forEachInParallel { ... }, which executes the given lambda function for each element of the list, but in parallel.
The compiler must generate code that will remain correct even in that severe case, so it doesn't make an assumption that the value of variable remains unchanged after the null check and thus cannot smart cast it.
However, List.forEach is quite different, because it is an inline function. The body of an inline function and the bodies of its functional parameters (unless the parameter has noinline or crossinline modifiers) are inlined at the call site, so the compiler could reason about the code in a lambda passed as an argument to inline function as if it was written directly in the calling method body making the smart cast possible.
It could, but currently, it doesn't. Simply because that feature is not implemented yet. There is an open issue for it: KT-7186.
Thanks to Ilya for the detailed explanation of the problem!
You can use the standard for(item in list){...} expression like this:
var max : Int? = null
val list = listOf(1, 2, 3)
for(item in list){
if (max == null || item > max) {
max = item
}
}
This looks like a compiler bug to me.
If the inline lambda parameter in forEach were marked as crossinline then I would expect a compilation error because of the possibility of concurrent invocations of the lambda expression.
Consider the following forEach implementation:
inline fun <T> Iterable<T>.forEach(crossinline action: (T) -> Unit): Unit {
val executorService: ExecutorService = ForkJoinPool.commonPool()
val futures = map { element -> executorService.submit { action(element) } }
futures.forEach { future -> future.get() }
}
The above implementation would fail to compile without crossinline modifier. Without it, the lambda may contain non-local returns which means it cannot be used in a concurrent fashion.
I suggest creating an issue: Kotlin (KT) | YouTrack.
The problem is that foreach creates multiple closures, each of which access the same max which is a var.
What should happen if max were set to null in another of the closures after the max == null check but before it > max?
Since each closure can theoretically work independently (potentially on multiple threads) but all access the same max, you can't guarantee it won't change during execution.
As this is in the top results for the error Smart cast to '<type>' is impossible, because '<variable>' is a local variable that is captured by a changing closure here is a general solution that worked for me (even if the closure is not inlined):
Sample showing this error:
var lastLine: String? = null
File("filename").useLines {
lastLine = it.toList().last()
}
if(lastLine != null) {
println(lastLine.length) // error! lastLine is captured by the useLines closure above
}
Fix: Create a new variable that is not captured by the closure:
var lastLine: String? = null
File("filename").useLines {
lastLine = it.toList().last()
}
val finalLastLine = lastLine
if(finalLastLine != null) {
println(finalLastLine.length)
}