https://kotlinlang.org logo
#announcements
Title
# announcements
a

Ashutosh Panda

10/14/2019, 5:16 PM
Copy code
var nullTest:Int?=null
nullTest?println("1"):println("0")
r

Ruckus

10/14/2019, 5:19 PM
if (nullTest != null) println("1") else println("0")
1
c

Casey Brooks

10/14/2019, 5:19 PM
The elvis operator “unwraps” a nullable value, it is not equivalent to the ternary operator (which intentionally does not exist in Kotlin). It’s used to get the non-null value of a variable, or else a default if it is null
Copy code
var nullTest: Int? = null
println("${nullTest ?: 0}")
If you don’t actually want the value of the variable itself, use the
if
expression Ruckus posted
p

Pablichjenkov

10/14/2019, 5:24 PM
I think you want the controversial
Copy code
nullTest?.let {
    println("1")
} ?: run {
    println("0")
}
😡 1
🤢 2
👎 4
Despite many people don't liking it, I think is the closest alternative to
if let
in swift. For this simple usage it is overkilling. But if you want to extract a non-null and operate on it, or else run some code. I haven't found anything better than bellow.
Copy code
nullTest?.let { nullTestNonNull ->
    nullTestNonNull.safeAccess()
} ?: run {
    println("nullTest is null, do something else")
}
a

Adam Powell

10/14/2019, 5:42 PM
println(if (nullTest != null) 1 else 0)
the reason people don't like
?.let {} ?: run {}
is because it does not behave like the if/else people try to use this as
if the expression inside the
let {}
evaluates to null the second
run
branch will execute
you'll say, "well this function I'm calling here is non-nullable, that can't happen!" and then you'll get into the habit, and you will experience this bug in your codebase at some point
that it's visually hideous as a pattern is a feature since it discourages this usage unless that double-evaluation in case of nulls is actually what you want
p

Pablichjenkov

10/14/2019, 5:45 PM
that's a good catch that I haven't thought of yet!
a

Adam Powell

10/14/2019, 5:47 PM
🙂
p

Pablichjenkov

10/14/2019, 5:47 PM
Thanks! I feel like we should copy
guard
and
if let
from swift.
👍 1
But I guess the language designers did not like it.
y

Yago Lasse

10/14/2019, 5:48 PM
Actually, if anything in the expression before the
?:
gets null, then the
run { }
is called
2
💣 2
So we can really use it as if not null/else
a

Adam Powell

10/14/2019, 5:50 PM
you cannot
y

Yago Lasse

10/14/2019, 5:50 PM
Just tested in Kotlin online with the code
Copy code
fun main() {
    val x: Int? = null
    x?.let { print("x is not null") } ?: run { print("x is null") }
}
and prints
x is null
p

Pablichjenkov

10/14/2019, 5:50 PM
I asked that question before and the other alternative is using
when
but in my opinion is extremely verbose for such a simple thing
a

Adam Powell

10/14/2019, 5:51 PM
if/else's behavior is mutually exclusive, if the first branch is taken the second one never will be. The let/?:/run thing is not mutually exclusive
✔️ 2
p

Pablichjenkov

10/14/2019, 5:54 PM
This is the real danger Adam is talking about:
Copy code
fun main() {
    
    val x: Int? = 1
    x?.let { 
        print("x is not null")
        null
    } ?: run { print("x is null") }
    
}
👆 1
If your let lambda returns
null
accidentally. Both branches execute.
✔️ 1
y

Yago Lasse

10/14/2019, 5:55 PM
In this case I agree with you guys. But, if u just want, for example, check something like
Copy code
myList?.takeIf { it.isNotEmpty() }?.forEach {
    // do some stuff
} ?: run { 
    // otherwise
}
I think it works as expected from a if/else
a

Adam Powell

10/14/2019, 5:56 PM
nope, because whether it works as "expected" is entirely dependent on the specific last line of
// do some stuff
4
well, not in this case because you used
forEach
- which kind of illustrates the point - it's very much not an if/else 🙂
1
which isn't to say it's not useful, it is!
1
y

Yago Lasse

10/14/2019, 6:04 PM
exactly! in case I used
let
instead of
forEach
then the
let
result would matter for the run block to be executed
Thanks for this information!
s

Steve Young

10/14/2019, 7:48 PM
if(nullTest!=null) println(“1”) else println(“0")
p

Pablichjenkov

10/14/2019, 7:49 PM
is it too crazy?
Copy code
public inline fun <T, R> T?.whenNonNullOrElse(
    nonNullBlock: (T) -> R,
    nullBlock: (() -> R)
): R? {
    
    return if (this != null) { nonNullBlock(this) } else { nullBlock()}
    
}
Then
Copy code
fun main() {
    
    val x: Int? = null
    
    val y: Int? = x.whenNonNullOrElse(
        { 
            println("x is not null")
            null
        },
        {  
            println("x is null")
            -1
        }
    )
    
    print("y = $y")
    
}
r

Ruckus

10/14/2019, 7:56 PM
But... why? What does that give you over a simple
if
? (Besides a massive reduction in readability of course.)
2
Copy code
fun main() {
    
    val x: Int? = null
    
    val y: Int? = if (x != null) { 
        println("x is not null")
        null
    } else {  
        println("x is null")
        -1
    }
    
    print("y = $y")
    
}
y

Yago Lasse

10/14/2019, 8:00 PM
if u want to use the value like
Copy code
if(x != null) {
     x.method()
} else {
     print("null")
}
Then u have to tell somehow to the compiler that x is not null, because it still thinks that x may have changed.
p

Pablichjenkov

10/14/2019, 8:00 PM
It allows to thread safe extract the receiver
y

Yago Lasse

10/14/2019, 8:01 PM
Thats why methods like
fun String.isNullOrBlank()
exists and use compiler contracts (and dont need
?
safe call operator)
r

Ruckus

10/14/2019, 8:01 PM
That's a lot of mental overhead to avoid
val _x = x
Those kinds of methods make sense if you're just returning a Boolean, not for much more.
p

Pablichjenkov

10/14/2019, 8:02 PM
The problem with
val _x = x
it start polluting the code
r

Ruckus

10/14/2019, 8:03 PM
Not any more than
x.whenNullOrElse({ ... }, { ... })
(Granted,
_x
should be replaced with a better name, but since when do we get names right? 🧌)
p

Pablichjenkov

10/14/2019, 8:09 PM
Even though
x.whenNullOrElse({ ... }, { ... })
is longer and more verbose its still a one liner shot. You can concatenated with stuff like
apply
and
also
Instead
val x = x
force you to write more than one line.
y

Yago Lasse

10/14/2019, 8:10 PM
Like Adam said up there, just use
x?.let { } ?: run { }
and take care of what you return inside
let
, and it will kinda work like a if not null/else
That way the compiler grants you that
it
is not null inside the
let
block.
r

Ruckus

10/14/2019, 8:13 PM
Personally, I see the unquenchable need to make everything in Kotlin into a one-liner an anti-pattern, but maybe I'm crazy.
1
p

Pablichjenkov

10/14/2019, 8:15 PM
@Yago Lasse I kind of buy that but remember the purpose of languages and compilers is avoid any possible human mistakes.
r

Ruckus

10/14/2019, 8:17 PM
Sure, but what mistake are you avoiding?
p

Pablichjenkov

10/14/2019, 8:17 PM
The one Adam mentioned about
x?.let { null } ?: run {}
y

Yago Lasse

10/14/2019, 8:18 PM
Agree, but there is one point that I am questioning: If we dont need the value, then we should do:
print(if(x != null) 1 else 0)
. But if we need the non null value, how to make it without a
!!
? Shadowing a variable is really the best option here?
r

Ruckus

10/14/2019, 8:18 PM
That is equally solved by declaring another variable. There's no advantage other than it's "one line".
p

Pablichjenkov

10/14/2019, 8:22 PM
I think the purpose of functional paradigm is using
if/else
the least possible
🚫 1
r

Ruckus

10/14/2019, 8:22 PM
Forgive my being blunt, but what does that have to do with anything? The question was about a replacement for a falsey ternary operation.
p

Pablichjenkov

10/14/2019, 8:27 PM
That's the answer! You shouldn't use
if/else
🙂. When you have a functional language that let you do cleaner stuff.
r

Ruckus

10/14/2019, 8:49 PM
But Kotlin isn't a functional language, and my whole point is it isn't cleaner.
2
p

Pablichjenkov

10/14/2019, 9:03 PM
Well, the reality Kotlin is a mix of functional and OO, although the tendency I think would be to make it more functionalish. What I mean by cleaner, is removing what is known by
branch creation
in code.
if/else
are a good contributors to it, when not used as expression.
Copy code
val xLocal = x
val yLocal = y

val z = if (xLocal != null) {
    if (yLocal != null) {
        (xLocal + yLocal)*(xLocal + yLocal)
    } else {
        xLocal*xLocal + 0
    }
} else {
    if (yLocal != null) {
        0 + yLocal*yLocal
    } else {
        0 + 0
    }
}
vs
Copy code
val z = x.whenNonNullOrElse(
    { it }
    , { 0 }
) + y.whenNonNullOrElse(
    { it }
    , { 0 }
).map{
    it*it
}
k

karelpeeters

10/14/2019, 9:03 PM
if
is completely functional, it doesn't have state or something like that.
vs
Copy code
val z = (xLocal ?: 0) + (yLocal ?: 0)
p

Pablichjenkov

10/14/2019, 9:05 PM
Thats true when used as expression but if it start nesting it becomes bad to read.
r

Ruckus

10/14/2019, 9:06 PM
@Pablichjenkov You never removed the if, you just moved it to another function call. How does that reduce branch creation?
k

karelpeeters

10/14/2019, 9:06 PM
I don't believe there are any cases where
whenNotNullOrElse
is cleaner than some other alternative.
p

Pablichjenkov

10/14/2019, 9:29 PM
@Ruckus That sample I posted is not good to show a branch creation issue because it is used as expression and the compiler will enforce you to exhaust all possible combinations. Now when not use as expression you may miss to declare one of the branches:
Copy code
var z = 0

 if (xLocal != null) {
    if (yLocal != null) {
        z = xLocal + yLocal
    } ...//missing branch, no compiling error but Business Logic error(worst)
} else {
    if (yLocal != null) {
        z = 0 + yLocal
    } else {
        z = 0 + 0
    }
}
@karelpeeters I agree that in many cases using the
if/else
expressiveness and the elvis operator will reduce verbosity. However, for the case when we want to apply an operation on a nullable receiver or else doSomething different, it makes it very concise to my liking. But well I agree with Ruckus I kind of diverge from the initial use case.