Fundamental Differences In Elvis Operator Between Adobe ColdFusion And Lucee CFML

Cyberdime
Published: March 17, 2022

At work, we use Lucee CFML; but, on my blog, I use Adobe ColdFusion. I enjoy having my feet in both camps because it forces me to have a more robust mental model of each language – seeing what is and is not the same. And, for the most part, things line up well. But, this morning I stumbled upon Charlie Cochran’s post on breaking changes in the Elvis operator in Adobe ColdFusion (ACF) which gave me pause. I panicked that I might be introducing subtle bugs into my applications. But, since he was talking about ACF, and I use Lucee CFML at work, I wanted to quickly test the two engines. And, oh chickens, they are very different!

To start, I’m a huge fan of the Elvis Operator ?: in ColdFusion. I love that it can be chained multiple times in a single expression and that it plays very nicely with the Safe Navigation operator. And, when trying to consume Golang’s omitempty nonsense, it’s really saved my bacon!

But, this was all in Lucee CFML, at work. And, it was all under the assumption that the “Elvis Operator” was really the same thing as the “Null Coalescing” operator. Which, apparently is not a great assumption.

In fact, Adam Cameron wrote about this divergence back in 2015. Heck, I even commented on that post in 2019; but, apparently I never committed it to my mental model.

Looping back to Charlie Cochran’s post, the breaking change in Adobe ColdFusion is that the Elvis Operator no longer works as a Null Coalescing operator (as of CF2016). It now only acts as a short-hand for the ternary operator. Lucee CFML, on the other hand, seems to continue to treat this as a Null Coalescing operator. Let’s look at the difference in the way the two engines handle Falsey Values:

<cfscript>
	echoVersion();
	// NOTE: I'm using echoArg() here because in Lucee, the left operand of the Elvis
	// operator has to be a Variable or a Function call - it cannot be a static value.
	echoValue( echoArg( 0 ) ?: "Ignored zero." );
	echoValue( echoArg( "0" ) ?: "Ignored string-Zero." );
	echoValue( echoArg( false ) ?: "Ignored false." );
	echoValue( echoArg( "false" ) ?: "Ignored string-False." );
	echoValue( echoArg( "no" ) ?: "Ignored string-No." );
	echoValue( echoArg( "" ) ?: "Ignored empty string." );
	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //
	// I output the current ColdFusion product version.
	public void function echoVersion() {
		var version = ( server.keyExists( "lucee" ) )
			? "Lucee CFML #server.lucee.version#"
			: "Adobe ColdFusion #server.coldfusion.productVersion#"
		;
		writeOutput( version & "<br /><br />" );
	}

	// I output the given value on its own line.
	public void function echoValue( required any value ) {
		writeOutput( value & "<br />" );
	}

	// I return the given argument.
	public any function echoArg( required any value ) {
		return( value );
	}
</cfscript>

As you can see, none of the values that I am testing are Null or Undefined. But, they do represent Falsey values and Falsey-esque values (ie, string versions of Falsey values). Now, when we run this in Adobe ColdFusion 2021 and Lucee CFML 5.3.8, we get the following output:

Adobe ColdFusion seen treating the Elvis operator as a short-hand for the ternary operator while Lucee CFML is seen treating the Elvis operator as a null coalescing operator.

Wow, that’s a striking difference! This must be what Brad Wood and Luis Majano are talking about when they say that the Elvis operator is basically unusable in frameworks like ColdBox (which have to be cross-engine compatible) due to the number of issues that it introduces.

Adobe ColdFusion is basically treating this:

( a ?: b )

… as a short-hand for this ternary operation:

( a ? a : b )

And, Lucee CFML is basically treating it as a short-hand for this ternary operation:

( isNull( a ) ? b : a )

ASIDE: I am over-simplifying these short-hands – the conditional checks are actually a bit more complicated than this and have to take undefined and complex values into account. But, this is trying to underscore the fundamental differences, not the behaviors that the two engines have in common.

These are fundamentally different behaviors. And, personally, I prefer Lucee CFML’s interpretation because Null Coalescing is really the operator that ColdFusion needs. If ColdFusion had full support for “Truthy values”, like JavaScript does, then we might be having a different conversation. But, until that happens, Null Coalescing is the real value-add – not just a short-hand for the ternary operator.

At least now I have a better mental model for the Elvis operator in ColdFusion. Hopefully this time I won’t forget.

Source: www.bennadel.com