The Safe Navigation Operator Checks Both Left And Right Operands In ColdFusion
Historically, I've always thought of the safe navigation operator - ?.
- as checking the left operand in an expression before continuing to evaluate the right operand. However, by sheer accident, I stumbled upon the fact that the safe navigation operator appears to check both the left and right operands. And, this appears to work in both Adobe ColdFusion and Lucee CFML.
Given the expression:
foo?.bar
I've always thought that the safe operator meant: If foo
is defined, return the evaluation of foo.bar
; and, if foo
is undefined, return null
.
But, it seems that this mental model is incomplete! Based on my accidental testing, it seems that this will return null
if either foo
or foo.bar
is undefined.
To see what I mean, take a look at this ColdFusion code:
<cfscript>
// Output the CFML runtime version.
if ( server.keyExists( "lucee" ) ) {
writeDump( "Lucee CFML #server.lucee.version#" );
} else {
writeDump( "Adobe ColdFusion #server.coldfusion.productVersion#" );
}
// Missing PARENT (safely navigating FROM an undefined reference).
// --
// Here, ColdFusion is checking the LEFT OPERAND (kablamo) for "nullness". This is
// how my mental model for the safe navigation operator was working.
writeDump({
value: variables.kablamo?.foo
});
// Missing KEY (safely navigating INTO an undefined key).
// --
// Here, ColdFusion is checking the RIGHT OPERAND (foo) for "nullness". This is an
// unexpected outcome according to my previous mental model.
writeDump({
value: variables?.foo
});
</cfscript>
In the first one, the safe navigation operator is short-circuiting the expression if the left operand, kablamo
, is undefined. But, in the second one, the safe navigation operator is short-circuiting the expression if the right operand, foo
, is undefined.
Before I ran this code, if I was asked to tell you what would happen, I would have assumed that the second one would have thrown an error for an undefined struct key. However, if we run this in both Adobe ColdFusion 2018 and Lucee CFML 5.3.7.48, we get the following output:
As you can see, both expressions return null / undefined in both Adobe ColdFusion and Lucee CFML. This was surprising!
What this means is that in my previous post on translating sparse Go JSON payloads, I could have replaced calls like this:
translateAppearanceModel( deviceModel.appearance ?: nullValue() )
... with calls like this:
translateAppearanceModel( deviceModel?.appearance )
... replacing the Elvis operator (null coalescing operator) with the safe navigation operator.
If this only worked in Lucee CFML, I would have considered it a bug. But, given the fact that it works in both CFML runtimes, I guess this is just how it's supposed to work; and that my mental model for it has always been insufficient. Which is great to know!
Want to use code from this post? Check out the license.
Reader Comments