What does "lazy" mean with this code in Kotlin? - kotlin

What is this code line doing?
fun<T:x>a.b(y: Int)=lazy{u.v<T>(y)}
I do not know what is 'lazy' doing or is 'lazy' something special in Kotlin.

fun<T:x>a.b(y: Int)=lazy{u.v(y)}
Let's break this down. First, let's reformat for readability :)
fun <T: x> a.b(y: Int) = lazy { u.v<T>(y) }
Now, let's go piece by piece.
fun means we're declaring a new method.
<T:x> means this is a generic method operating on type T where T is constrained to be of type x.
a.b means this is an extension function named b on type a.
(y: Int) means that the defined function b takes a single argument named y of type Int.
= is expression body syntax - shorthand for returning a short line of code. This means that a.b will return the value that is the result of evaluating lazy { }
lazy is a Kotlin standard library function that delays the evaluation of the function provided to it until it's needed and then caches the result. The return value of this function is actually a type Lazy that wraps the provided function.
{ u.v<T>(y) } is the function that will be executed by the Lazy object when it's value is obtained the first time and the return value of u.v<T>(y) will be saved as the lazy object's value.
Phew! So what does that mean? Let's look at an example. Suppose we add a print statement to the function to see when it's called.
fun <T: x> a.b(y: Int) = lazy {
println("Executing 'b'")
u.v<T>(y)
}
Now if you tried to use it:
fun main() {
val a = A<T>() // Assume some type T
val lazyObject = a.b<T>(42) // Call the extension method that returns a `Lazy`
// Get the value from the lazy object - prints "Executing 'b'",
// executes `u.v<T>(y)`, caches the result, returns it - then print it
println(lazyObject.value)
// Get the value from the lazy object again. This time, DOES NOT print
// "Executing 'b'", DOES NOT execute `u.v<T>(y)`, and just returns the
// result that was already computed and cached, then print it
println(lazyObject.value)
}
So, in summary, the code you posted is creating an extension method that returns a Lazy object that, when queried for its value, executes the lambda it's initialized with and caches that result for later use.
Hope that helps!

Related

Kotlin is not able to change var using a method

Could someone explain why can't I change the value of var in that case ?
fun main(args: Array<String>) {
var number = 3
changeNumber(number)
}
fun changeNumber(number: Int) {
number = 4 //here I see a warning "val cannot be reassigned"
}
By passing a "number" to your function you "pass-by-value" NOT "pass-by-reference", the function does not know where in memory your main number is stored, therefore it cannot change it's value
you can see more about the subject here and here
There is absolutely no way to do it directly. Kotlin copies a value for scalar types (Double, Float, Boolean, Int, etc.). So any internal changes are lost.
For others types Kotlin copy a reference of parameter passed to the function. So any property/field alteration of parameter, also changes the caller parameter.
So you can wrap up your number in for this example an IntegerHolder and change the value that is kept in the reference.
data class IntegerHolder(
var v:Int
)
fun main() {
var a:IntegerHolder = IntegerHolder(2)
changeNumber(a)// Echange a value
print(a.v)
}
fun changeNumber(a:IntegerHolder) {
a.v = 5
}
Just in case you find the other answers a bit confusing, I'll add that you don't need to know about what's a scalar or passed by value. Those are under-the-hood optimizations that the compiler does but don't change the logical behavior of your code.
Kotlin works only with references, not pointers. What you're trying to do is what you can do with pointers in a language like C or C++. In those languages, you can pass a pointer to a function. A pointer is not the value of a variable, but the memory address of the variable itself so other functions can modify what the variable address is pointing at.
That's flat out not supported in Kotlin. You can only pass references. You are passing the object that the variable is pointing to, but you can't do anything to that variable itself. You are not passing a copy of that object, so if that object is mutable, you could change the values of properties inside it and the original function could see those changes by inspecting the object again. But many simple classes like Int, Float, Double, and String are all immutable, so it's logically irrelevant that you aren't passing a copy (and that's why Kotlin under-the-hood can optimize by passing actual values for some of these, called "inline classes").
A couple of workarounds for this limitation:
Mutable wrapper class. Use this in as your variable type and function parameter type.
data class IntWrapper(var value: Int)
fun main(args: Array<String>) {
val number = IntWrapper(3)
changeNumber(number)
println(number.value)
}
fun changeNumber(number: IntWrapper) {
number.value = 4
}
Pass a function that can modify your variable. The setter function is the parameter for your function that changes the variable. (The difference between pointers and what we do here is that the function that changes the variable doesn't actually know that it's changing a variable. It's just calling the function that was passed to it, which could be doing anything it wants with the provided number.)
fun main(args: Array<String>) {
var number = 3
changeNumber { number = it }
println(number)
}
fun changeNumber(numberSetter: (Int)->Unit) {
numberSetter(4)
}
But it's not very often that you'll need to do one of these. It's more common to write functions that provide a return value, and you can use that value to reassign the variable. This strategy is more robust. It provides better encapsulation, which naturally makes your code less bug-prone.
fun main(args: Array<String>) {
var number = 3
number = produceNewNumber()
println(number)
}
fun produceNewNumber(): Int {
return 4
}

Is there a simple null safe operator for Kotlin function references?

I'd like to pass a function reference on a nullable object. To take an Android example, say I want to use Activity#onBackPressed from a fragment that is a child of that actvity.
If I wanted to invoke this function, I could easily do
activity?.onBackPressed()
However, say I wanted to pass that as a reference instead:
val onBackPressedRef = activity::onBackPressed
This gives the familiar null safe error of Only safe or non null assserted calls are allowed...
I can get the error to go away with the following, but using !! is obviously not ideal:
val onBackPressedRef = activity!!::onBackPressed
Attemping activity?::onBackPressed was my first instinct, but this also breaks with several errors, where the interpreter seems confused.
val onBackPressedRef = activity?.let { it::onBackPressed }
This last variation works, but it's a lot more ugly than just using ?::. I checked all the docs I could find, but I feel like I'm missing something. Any ideas?
You are right, there is no ?:: operator in Kotlin.
You have several alternatives:
1. let and run
Thus, you have to use a helper function. Instead of let(), you can also use run(), making the expression a tiny bit shorter:
val onBackPressedRef = activity?.let { it::onBackPressed }
val onBackPressedRef = activity?.run { ::onBackPressed }
But keep in mind that either way, the invocation will be more verbose, too:
onBackPressedRef?.invoke(args)
Thus you should ask yourself, if this is really what you want, or if a no-op function call is also acceptable.
2. Closures
You could use a closure -- this will change semantics however:
val onBackPressedRef = { activity?.onBackPressed() }
Here, onBackPressedRef is not nullable anymore, so you can call it using the () operator, and in case of null activity it will have no effect.
3. Helper function
If function references with nullable objects are something you encounter a lot, you can write your own little abstraction:
// Return type: () -> Unit
fun <T> funcRef(obj: T?, function: T.() -> Unit) = { obj?.function() }
This trades a different syntax for a non-null function variable:
// activity can be null
val onBackPressedRef = funcRef(activity, Activity::onBackPressed)
// Callable directly
onBackPressedRef()

Using implicit variable in function literal

I started reading Kotlin course book. I stopped on function literals. Here I have a code:
val printMessage = { message: String -> println(message) }
printMessage("hello")
printMessage("world")
Then I have an information that I can omit parameter type:
{ message -> println(message) }
And now I have next step:
"In fact, Kotlin has a neater trick. If there is only a single parameter and the type can beinferred, then the compiler will allow us to omit the parameter completely. In this case, itmakes the implicit variable it available:
{println(it)}
And now after using this code I get an error "unresolved reference: it" and "too many arguments for public operator fun invoke(): ??? defined in kotlin.Function()":
val printMessage = {println(it)}
printMessage("print something")
My question is how to use implicit variable in single paramenter function literal?
See the Kotlin documentation, specifically where it says:
If the compiler can figure the signature out itself, it is allowed not
to declare the only parameter and to omit ->. The parameter will be
implicitly declared under the name it.
In your case, the compiler (at least up to current version 1.3.31) can't figure the signature out itself:
val printMessage = {println(it)}
But if you give your printMessage variable an explicit type, it will work:
val printMessage: (String) -> Unit = { println(it) }
You always need to provide all information about all generic parameters. If you want to omit it, it needs to be inferable from some other part of the code. The only information you provide though is that you want printMessage to be a lambda. So it assumes it to be of type ()->Unit. This is because you don't declare a parameter for the lambda itself. The implicit parameter it is therefore not usable.
val printMessage = { it: String -> println(it) }
val printMessage: (String)->Unit = { println(it) }
Simply put: If you're inside a lambda with one parameter, the implicit it can be used as this parameters name, but a reference named it within the body of the lambda doesn't declare the single parameter.

Why the variable can't be initialized correctly in inline function as in java?

We know the lambda body is lazily well, because if we don't call the lambda the code in the lambda body is never be called.
We also know in any function language that a variable can be used in a function/lambda even if it is not initialized, such as javascript, ruby, groovy and .etc, for example, the groovy code below can works fine:
def foo
def lambda = { foo }
foo = "bar"
println(lambda())
// ^--- return "bar"
We also know we can access an uninitialized variable if the catch-block has initialized the variable when an Exception is raised in try-block in Java, for example:
// v--- m is not initialized yet
int m;
try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}
System.out.println(m);// println 2
If the lambda is lazily, why does Kotlin can't use an uninitialized variable in lambda? I know Kotlin is a null-safety language, so the compiler will analyzing the code from top to bottom include the lambda body to make sure the variable is initialized. so the lambda body is not "lazily" at compile-time. for example:
var a:Int
val lambda = { a }// lambda is never be invoked
// ^--- a compile error thrown: variable is not initialized yet
a = 2
Q: But why the code below also can't be working? I don't understand it, since the variable is effectively-final in Java, if you want to change the variable value you must using an ObjectRef instead, and this test contradicts my previous conclusions:"lambda body is not lazily at compile-time" .for example:
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function
// v--- a compile error thrown: variable is not initialized yet
println(a)
So I only can think is that the compiler can't sure the element field in ObjectRef is whether initialized or not, but #hotkey has denied my thoughts. Why?
Q: why does Kotlin inline functions can't works fine even if I initializing the variable in catch-block like as in java? for example:
var a: Int
try {
run { a = 2 }
} catch(ex: Throwable) {
a = 3
}
// v--- Error: `a` is not initialized
println(a)
But, #hotkey has already mentioned that you should using try-catch expression in Kotlin to initializing a variable in his answer, for example:
var a: Int = try {
run { 2 }
} catch(ex: Throwable) {
3
}
// v--- println 2
println(a);
Q: If the actual thing is that, why I don't call the run directly? for example:
val a = run{2};
println(a);//println 2
However the code above can works fine in java, for example:
int a;
try {
a = 2;
} catch (Throwable ex) {
a = 3;
}
System.out.println(a); // println 2
Q: But why the code below also can't be working?
Because code can change. At the point where the lambda is defined the variable is not initialized so if the code is changed and the lambda is invoked directly afterwards it would be invalid. The kotlin compiler wants to make sure there is absolutely no way the uninitialized variable can be accessed before it is initialized, even by proxy.
Q: why does Kotlin inline functions can't works fine even if I initializing the variable in catch-block like as in java?
Because run is not special and the compiler can't know when the body is executed. If you consider the possibility of run not being executed then the compiler cannot guarentee that the variable will be initialized.
In the changed example it uses the try-catch expression to essentially execute a = run { 2 }, which is different from run { a = 2 } because a result is guaranteed by the return type.
Q: If the actual thing is that, why I doesn't call the run directly?
That is essentially what happens. Regarding the final Java code the fact is that Java does not follow the exact same rules of Kotlin and the same happens in reverse. Just because something is possible in Java does not mean it will be valid Kotlin.
You could make the variable lazy with the following...
val a: Int by lazy { 3 }
Obviously, you could use a function in place of the 3. But this allows the compiler to continue and guarantees that a is initialized before use.
Edit
Though the question seems to be "why can't it be done". I am in the same mind frame, that I don't see why not (within reason). I think the compiler has enough information to figure out that a lambda declaration is not a reference to any of the closure variables. So, I think it could show a different error when the lambda is used and the variables it references have not been initialized.
That said, here is what I would do if the compiler writers were to disagree with my assessment (or take too long to get around to the feature).
The following example shows a way to do a lazy local variable initialization (for version 1.1 and later)
import kotlin.reflect.*
//...
var a:Int by object {
private var backing : Int? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int =
backing ?: throw Exception("variable has not been initialized")
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
backing = value
}
}
var lambda = { a }
// ...
a = 3
println("a = ${lambda()}")
I used an anonymous object to show the guts of what's going on (and because lazy caused a compiler error). The object could be turned into function like lazy.
Now we are potentially back to a runtime exception if the programmer forgets to initialize the variable before it is referenced. But Kotlin did try at least to help us avoid that.

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)
}