kotlin - run vs elvis operator - what is the difference? - kotlin

im trying to understand the difference between the following two pieces of code in kotlin:
myVar?.let { print(it) } ?: run { print("its null folks") }
vs
myVar?.let { print(it) } ?: print("its null folks")
are they equivalent ? is run just so we can use a block of code and the the other is for just a single statement ?

Yes, they are equivalent. run allows you to use multiple statements on the right side of an elvis operator; in this case there's only one, so run is not needed.

Related

Terminal operator for Kotlin sequence that discard the output?

I'm operating on very large Kotlin sequence, I'm executing my logic on every step of the sequence and I never need to keep the whole sequence in memory.
Currently my code looks like this
hugeSequence
.filter { ... }
.map {...... }
.onEach {
callExpensiveOperation(it)
}
.toList() <- this feels wrong
The toList() at the bottom is the terminal operator, but I'm worried that Kotlin may try to create a huge list in memory, before realising that I'm not even assign the result value of that operation.
Is there any other terminal operator I can use just to trigger the sequence to start?
Use forEach instead of onEach. It is the terminal equivalent of onEach.
hugeSequence
.filter { ... }
.map {...... }
.forEach {
callExpensiveOperation(it)
}

Elvis operator with Run in kotlin

Im having a bit of trouble with this situation
a?.let {
b?.let { }
}?: run { }
The thing is, if "b" is null, the run block is executed, even though the elvis operator is referencing the "a" let.
I already tried to use "apply" instead of "run", same thing happens
The reason is that the let function returns whatever its last expression is. If the last expression evaluates to null (as b?.let would if b is null or the last line of that inner let evaluates to null), then the second part of the Elvis operator will be evaluated.
The solution is to never follow up a scope function call with an Elvis operator. It would work OK with also instead of let since it doesn't return the lambda result, but it's still obtuse-looking code that's hard to read. It's such an ugly pattern to use that people make memes about how ridiculous it is.
For this particular case, I would refactor your code as follows.
val a = a
if (a != null) {
b?.let {
//...
}
} else {
//...
}

Pass no value to function with default parameters

I have this Kotlin function:
fun doSomething(user: User = defaultUser) {
//do something
}
and I call it from another place:
val user: User? = getUser()
if (user == null) {
doSomething()
} else {
doSomething(user)
}
Is it possible to improve this code? I think this "if/else" is a little bit messy. Is possible to do something like this?
doSomething(user ?: NoValue)
You can cut it down to user?.run(::doSomething) ?: doSomething() (if doSomething doesn't return null) but I don't know why you'd want to!
Honestly the if/else reads nice to me, stick it on one line without the braces and it's nice and compact. Unfortunately I don't think you can conditionally add parameters into a function call (and handling default parameters can get unnwieldy when you have a few).
I agree with #benjiii, it might be better to have a nullable parameter and handle the default internally, if you don't need to use null as a legit value
You could do something like this:
getUser()?.let { // user is not null
doSomething(it)
} ?: run { // user is null here
doSomething()
}
(cf: Swift 'if let' statement equivalent in Kotlin)
I don't think you could do something shorter without making the code hard to understand Edit 2: Actually you can, see the comment
Edit: I would personally handle the nullable variable inside the function like this:
fun doSomething(user: User?) {
val correctUser = user ?: defaultUser
//do something
}
so you can use the function like this:
doSomething(getUser())
I agree with cactustictacs, just putting it on one line is clear and simple. However, if you use it often and it's bothering you, it's easy enough to wrap it in a function without the default parameter:
fun doSomethingSensibly(user: User?) =
if (user == null)
doSomething()
else
doSomething(user)
Which can be used as:
doSomethingSensibly(getUser())

Kotlin let versus != null

What is the difference between using kotlin's object?.let {...} versus if(object != null) {...}? I've run into the situation where using let on a non-null equates to false and the block is not executed but using the if statement with the same object results in properly identifying that the object non-null and the block is executed.
Do they differ on the low level somehow?
Under the hood, object?.let { } will be compiled to if (object != null) { }. You can try to check the kotlin bytecode.
IMHO, using let also may benefit you to chaining function which makes your code declarative, and I think it is more readable.
One of the scenario where let keyword is preferable when you are reading the list of items that also contains null values and you wants to print only not-null values.
For Example:-
var array = arrayOf("StackOverflow", "Kotlin", "Android", null, "Jetpack", null)
for (item in array) {
item?.let {
print("$it ")
}
}
Output:
StackOverflow Kotlin Android Jetpack
Code inside for loop is like saying "if item is not null, let's print its value". Therefore ?.let allows you to run code for a value that's not null.

Option Chaining instead of if/else

Is there a more succint way to write the following code using option chaining and/or the elvis operator?
email.addSubject(if (creator != null) String.format( inviteDescription, creator) else String.format(inviteDescriptionNoCreator, group))
It feels like there should be.
Using the normal IF expression
val subject = if (creator != null) {
inviteDescription.format(creator)
} else {
inviteDescriptionNoCreator.format(group)
}
email.addSubject(subject)
The Elvis Operator
val subject = creator?.let {
inviteDescription.format(it)
} ?: inviteDescriptionNoCreator.format(group)
email.addSubject(subject)
If the goal is to write the shortest code possible then you could go with a single line Elvis operator. But if the goal is a readable code, I would choose the simple if expression or a multi line Elvis operator. I would even go one step ahead and move it to a separate method. But whatever you choose, please don't write everything in a single long line, rather put it in separate lines.
Just taking advantage of ?. and ?: gives us the following:
email.addSubject(creator?.let { String.format(inviteDescription, it) } ?: String.format(inviteDescriptionNoCreator, group))
Unfortunately, that's still quite long and is arguably not much easier to read. You could shave off a bit more by using the String.format extension function:
email.addSubject(creator?.let { inviteDescription.format(it) } ?: inviteDescriptionNoCreator.format(group))