I start googling for “Kotlin ternary” operator when have to write “== null” over and over. It would be sweet if there was a postfix operator for this:
if (a?) b else c
I start googling for “Kotlin ternary” operator when have to write “== null” over and over. It would be sweet if there was a postfix operator for this:
if (a?) b else c
I find it confusing. I would expect that b
would be called when a
is not null.
On the other hand, I can image using this function
if (a?) b else c
as equivalent to
if (a != null) b else c
since we are already using question mark with nullable types (as?
or foo?.bar
– here bar
is called when foo
is not null.).
I know this is specifically about null checking and maybe I’m incorrectly broadening the issue, but I find myself thinking about truthiness in languages in general. So please excuse the rant
Personally I love having to explicitly write the full a == null
or a != null
.
Over time I’ve learned to dislike “truthy”-ness of languages that allow more than a Boolean to be implicitly considered true or false (or further allowing you to override the truthiness of a class).
The Kotlin stdlib functions make it even more readable and clear with conventions like Collection.isEmpty(), String.isEmpty, String.isBlank and more. Those functions replace the old written out conditionals that were simple but hard to read better than the truthiness approach.
Although it can feel odd at first not being able to write conditions with only an object, if (a)
, in time you may come to prefer stating your condition as a method, for example if (a.isValidUser())
.
IMHO almost if on a non-boolean object could have the actual condition written out more clearly. Still a bit off of the original issue of checking for null…
Okay so back on topic a bit:
For having a if (shortcut_for_is_null)
idea, I do see the value of being able to quickly and easily state such a common condition.
My main concern is that although it saves characters, just like truthiness it may sacrifice the reader in favor of the writer.
I mean that the clearest way I can imagine writing “if a is null” is the fully written if (a == null)
. Yes it would save 7 characters, but I don’t see that as much of a gain when we already have live templates (and even brand new trailing live templates ).
I do like the potential of being able to expressly use a value when it is null. And the a?
syntax would be a bit backwards compared to how it’s used by safe calls and safe casts.
Maybe we should consider use cases outside of “if a is null”. It could be that there’s more benefits for expressing explicit null elsewhere, for example maybe it would help with null assignment (instead of a ?: a = 5
maybe a? = 5
). Again using a syntax could feel a bit backwards.
This is kind of off topic of OPs idea since it wouldn’t change null checks initially.
What’s the use case here?
In my experience, if you’re checking whether something’s null, then you’re nearly always going to want to use that object, not some other. That’s why we have the elvis operator:
a ?: b
If you frequently find yourself checking something for null without actually using its non-null value, then that may indicate deeper design issues.
That gets into the whole thorny issue of nulls in language design which I won’t step into. I found myself adding a lot of nullable properties recently with micronaut web interfaces which wouldn’t accept missing property otherwise even though I defaulted them to Null.
Doesn’t groovy just support:
if (a) b else c
I’ve seen this in kotlin code few times from colleagues:
when(a) {
null -> b
else -> c
}
I think you can do a?.let { b } ?: c
@arocnies Rant stands. I think it’s worth being careful about truthiness and maybe languages like groovy went too far at the expense of the reader. But, why not add a truthiness operator? it would let the reader know that a short-hand is being used without getting into the verbosity of Java.
A truthiness operator would let the reader know you’re doing a shorthand. Another way to do that would be some truthy function or the currently available operators. For example, instead of some new operator, we could write:
if (isNull(a)) { /* ... */ }
if (a.isNull) { /* ... */ }
if (+a) { /* ... */ }
I wouldn’t consider any of these options better than the plain old
if (a == null) { /* ... */ }
If our goal is to save on typing, we aren’t doing a ton. There’s a slight saving of characters by using the unaryPlus
operator but it’s so easy to type the others that it’s doesn’t seem worth it. Even though I prefer the standard (a == null)
, my second favorite would probably be (a.isNull)
due to my internal voice vocalizing the line as “okay, so then if a is null, then…”–any other form makes me do a little odd translation to that wording.
A dedicated new operator might help over unaryPlus
. We still save the 6 or so characters and we might have an easier time reading it if it’s only used to mean “a is null”.
A new operator, compiler changes, and not more clear to the reader. All of this to save 6-8 characters. It’s true checking for null is a common condition, so maybe we get to save writing those 6 characters in many places around our code–but the price for such a feature still seems a bit costly for so little gained.
If it’s common to be slowed down with writing out a null check then the writing is the real problem. The solution can address tooling the text editing experience instead of trying to change the language. Intellij has live templates and even postfix templates.
With templates, all you need to write a null check is a.nn
which would expand to if (a != null)
, or a.in
which would expand to if (a == null)
. I don’t remember the exact template shorthand since I’m on mobile but you can always add a custom template.
For me, these kinds of tooling and editor support gives enough of a shorthand for the writer without obscuring the rather simple null check of if (a == null)
.
A bit of a side rant here as well. I’ve noticed it’s not uncommon for people to complicate a simple null check. For example people start to write a?.let { }
instead of if (a != null)
. Or they try to implement their own if like higher order function.
There is a time to use the alternates but it’s always good to be aware of the trap of complicating a plain old null check.
Or you may use
a?.let { b } ?: run { c }
if c
is a compound expression, but it is not much readable.
Another alternative
b.takeIf { a != null } ?: c
What about
infix fun <A : B, B> A?.orElse(value: B): B =
this ?: value
inline infix fun <A : B, B> A?.orElse(func: () -> B): B =
this ?: func()
a?.let { b } orElse { c }
a?.let { b } orElse c
Warning: if a
is not null and b
is null then you get c
, which is a little different than if (a != null) b else c
.