DEV Community

Cover image for Code Smell 212 - Elvis Operator
Maxi Contieri
Maxi Contieri

Posted on • Updated on • Originally published at maximilianocontieri.com

Code Smell 212 - Elvis Operator

Your code is not safer using this operator

TL;DR: Don't propagate nulls.

Problems

  • NULL propagation

  • Harder to read code

  • Hacky code

Solutions

  1. Remove the nulls.

  2. If you can't remove it, deal explicitly with them.

Context

The Elvis operator is also known as the null-coalescing operator or the null-safe operator.

It is a shorthand operator used in some programming languages to simplify null-checking.

The Elvis operator takes the form of ?. and is used to access a property or method of an object only if that object is not null.

If the object is null, the operator returns null without attempting to access the property or method, thus avoiding a potential null reference exception.

Sample Code

Wrong

val shipTo = address?: "No address specified"

Enter fullscreen mode Exit fullscreen mode

Right

val shipTo = if (address != null) address else "No address specified"

// This keeps the billion-dollar mistake error 
Enter fullscreen mode Exit fullscreen mode

Detection

[X] Automatic

We can detect this operator usage and replace them with more strict checks.

Tags

  • Null

Conclusion

The code can be difficult to follow and may require additional comments or explanations to make it clear what is happening.

The operator hides potential errors or bugs in the code.

For example, if an object is null and the Elvis operator is used to return a default value, this may mask the fact that there is a problem with the code that is causing the object to be null in the first place.

n several languages, such as Common Lisp, Clojure, Lua, Object Pascal, Perl, Python, Ruby, and JavaScript, the OR operator (typically || or or) has the same behavior as the above: returning its first operand if it would evaluate to true in a boolean environment, and otherwise evaluating and returning its second operand.

When the left hand side is true, the right hand side is not even evaluated; it is "short-circuited." This is different than the behavior in other languages such as C/C++, where the result of || will always be a boolean.

Relations

More Info

Disclaimer

Code Smells are my opinion.

Credits

Photo by Susan Mohr on Unsplash


You can't communicate complexity, only an awareness of it.

Alan Perlis


This article is part of the CodeSmell Series.

Top comments (1)

Collapse
 
moopet profile image
Ben Sinclair

This comes back to the idea of avoiding nulls or "undefineds". I agree there, but...

There are no functional differences between your "wrong" and "right" examples. One isn't opening us up to any more potential bugs. If you write a long complex chain of things using conditionals then it'll be harder to read, sure, but that's the same with the explicit version. In fact, the elvis version means you don't have to reassign a variable, which means you can declare it a const in Javascript more easily (random example):

let shipTo = null;
if (address != null) {
    shipTo = address.zipCode();
}   
Enter fullscreen mode Exit fullscreen mode

vs

const shipTo = address?.zipCode?.();
Enter fullscreen mode Exit fullscreen mode

A good reason against using elvises (is that the plural?) is that they look pretty damn ugly when you're calling a function, and they make it less obvious what's a property value and what's a function when glancing over code, since our brains are trained to use . as a separator and this is introducing a new one. You could make it clearer by making the function name explicit (i.e. address?.getZipCode?.()). I think conditional function execution like that harms readability.

The problem with missing properties is that they often come from other sources, like API responses. One option is to validate or sanitise those responses first, which would mean we could throw an error if the API response changed, and we could then "safely" assume that all properties we cared about existed in our code.

Personally, if I was genuinely passed something unknown, I'd be happy to use elvis operators for a couple of levels of depth when declaring local variables, versus something more explicitly conditional if it needed anything more than that - if my function was relying on a deep property which might not be present, I'd want to split the logic for that out to its own function.