Kotlin multiple variable let, using the previous variables in the next let - kotlin

I want to be able to have multiple let and the next let is able to use the previous variable only if its not null. The reason I want this is because I want only one :? for all the lets. Is this possible?
Example on how I want it to be:
fun example() {
firstVariable?.let a -> &&
exampleFunction(a, 3)?.let { a, b ->
// Use a and b freely since they are not null and not optionals
} ?: run {
// Runs if either one of the let is failing
}
}
// a is NOT optional
fun exampleFunction(a: Int, b: Int?): Int? {
if (b == null) {
return null
}
return a + b
}
Stupid example, but its just to show what I need... I want to check if the first variable is null AND to run a function that returns an optional with a non-optional parameter which is the first variable - if either of these fail, I want to run something else.
I know how to do this without let, but I am wondering if it's possible or planned to be able to do this? (It's possible in Swift).
How to do it in Swift:
// First check a, then use a in the same expression if its validated
if let a = firstVariable,
let b = exampleFunction(a) {
// Use a and b as non-optionals
} else {
// a or b failed
}

You propably missunderstood how let works. I am going to explain a bit. In short the desired behaviour is not possible in kotlin or at least you can not idiomatically emulate it without any drawbacks whatsoever.
I don't know swift but it seems as if the let used there is some sort of syntax construct offered by the language itself. It allows you to define a variable with some local scope and can be chained (like the short circuiting &&).
In Kotlin however let is just a normal function.
See the documentation. It's basically nothing more than
fun <T, R> T.let(block: (T) -> R): R = block(this)
It allows to call a function with a normal parameter as a function with a receiver type.
The actual null check is done with the ?. operator.
It takes an optional/nullable value as left hand side operand and either short circuits returning null or call the function on the right hand side with the non-null left hand side as receiver type. let is just one possible function to call here.
The similar ?: operator takes an optional/nullable LHS operand and returns this value if it is not null or it evaluates the expression on the RHS.
One way to get those variables defined is by nesting lets:
firstVariable?.let{a -> exampleFunction(a, 3)?.let{b -> a + b}} ?: run{}
where a + b is just an example of using both values. This however becomes unhandy if it's longer than one line. If you still want to define local variables you can create a block with run and use jump statements on the right side of ?:
run {
val a = firstValue ?: return#run null
val b = exampleFunction(a, 3) ?: return#run null
return#run a + b
} ?: run{}
While the above code looks really ugly with all those return#run null repititions there might be ways to reduce the amount of repeating code e.g. by using a anonymous function (to get rid of the #run part) or return Unit and safe the last value with some side-effect operation. (to get rid of the null and the last return statement)

You could benefit on Kotlin and write sot of extension function for your case. vararg as we dont know how many variables we want to pass, then check if all of them are not null and if so, return all of them. If any of the vars will be null, then nothing happens.
fun <T: Any> multipleLetCheck(vararg variables: T?, block: (List<T>) -> Unit): Unit? {
return if (variables.all { variable -> variable != null }) {
block(variables.filterNotNull())
} else {
null
}
}
// usage
multipleLetCheck(firstVariable, 3){ (firstVariable, secondVariable) ->
// work with firstVariable and secondVariable
} ?: run {
}

Related

Check if a Kotlin variable is a function

I am currently learning Kotlin and am working through the By Example Guide. In the chapter Functional/Higher-Order Functions it explained how functions themselves can return functions with this example:
fun operation(): (Int) -> Int {
return ::square
}
fun square(x: Int) = x * x
fun main() {
val func = operation()
println(func(2))
}
Since I had previously learned to check the type of a variable in a "when" block, I tried to do the same here. Checking if a variable is of type function.
fun operation(): (Int) -> Int {
return ::square
}
fun square(x: Int) = x * x
fun main() {
val func = operation()
when (func){
is fun -> println(func(2))
else -> println("Not a function")
}
}
But this throws an error "Type expected", I guess because fun in itself is not a type.
I tried searching for things like "kotlin check if variable is function" but all I could find where guides on how to check for primitives or classes, nothing even mentioned functions.
Let's suppose you know nothing about func. (func is of type Any) You can easily check that it is a function by doing:
if (func is Function<*>) {
...
}
Or similarly use is Function<*> in a when branch.
However, this doesn't tell you the number or types of parameters, or the return type. Since you want to call the function with an Int here, it is important that you also check that the function has exactly one parameter of type Int. You can add a number after the word Function to check for a specific number of parameters,
if (func is Function1<*, *>) {
...
}
But that's where the simple stuff stops.
It is very hard to check for parameter types. You cannot just do this:
if (func is Function1<Int, Int>) {
...
}
Because generics are erased, the runtime is not able to distinguish between a Function1<Int, Int> and a Function1<Foo, Bar>, so you cannot check for a specific type parameter using is.
The only way I can think of is unfortunately, reflection.
// JVM only
if (func is Function1<*, *> &&
(func as? KFunction<*> ?: func.reflect())?.parameters?.singleOrNull()?.type == typeOf<Int>()) {
// this is an unchecked cast, which means the runtime won't check it
// but it is fine, because the code above checked it
println((func as Function1<Int, *>)(2))
}
operation can either return a KFunction, like your ::square, or a lambda. If it returns a lambda, the reflect experimental API (You'd need #OptIn(ExperimentalReflectionOnLambdas::class)) is used to turn it into a KFunction.
After we have a KFunction, we can inspect its single parameter (if it has one) and check if it is Int.
So checking for specific types of functions is quite a pain. I would recommend that you change your design to avoid doing this, if you ever find yourself needing to do this.
You can do general check is Function<*> if you just need to find out given func object is a function or not. Also you can do restricted check for more specific function signatures, e.g. check it's a function with N arguments.
In the following code checks are arranged from more specific to less specific:
fun main() {
val func = operation()
when(func) {
is Function2<*,*,*> -> println(func(4)) // function with 2 arguments
is Function1<*,*> -> println(func(3)) // function with 1 argument
is Function<*> -> println(func(2)) // function in general
else -> println("Not a function")
}
}
And the output of this code is 9 because func is both a "Function" & a "Function with 1 argument", but "Function with 1 argument" condition is matched earlier.
P.S. Function22 (f with 22 arguments) is largest built-in into Kotlin

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

Is the a way to use the default value of a non-nullable parameter when null is passed as an argument?

I looking for a way to have default values take the place of nulls when passed as arguments. My motivation is purely to reduce the amount of code written (I want to avoid having overloaded functions/constructors or manual 'if null' checks)
My use case is within a Spring RestController, I want default values of a method called by the controller to be used without needing to state those default values outside the function.
I thought perhaps that using named parameters might provide this functionality but my experiments show otherwise. Perhaps there is a way with the elvis operator?
Example Code:
fun someFunction(first: Long = 1, second: Int = 2 ) {
// Do something
}
#GetMapping
fun someEndpoint(#RequestParam("first") firstParam: Long?):ResponseEntity<Any> {
someFunction(firstParam) // Attempt 1: "Required: Long\n Found: Long?
someFunction(first = firstParam) // Attempt 2: Same error
}
Hopefully you can help
There aren't any specific language features that would do this for you, the default argument mechanism isn't connected to nullability in any way.
However, you can achieve this in a more manual fashion by making your parameters nullable, and immediately substituting default values inside the function if they're null:
fun someFunction(first: Long? = null, second: Int? = null) {
val actualFirst: Long = first ?: 1
val actualSecond: Int = second ?: 2
// Do something with actualFirst and actualSecond
}
The #RequestParam annotation has a default value option named "defaultValue".
you can use it like so:
#GetMapping
fun someEndpoint(#RequestParam(name = "first", defaultValue = "1") firstParam: Long):ResponseEntity<Any> {
someFunction(firstParam) // firstParam equals to 1 if null was passed to the endpoint
}

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

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