Skip to main content
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Doug Neiner
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with: Doug Neiner

Using The Safe Navigation Operator To Propagate NULL Arguments In ColdFusion

By
Published in

One pattern that has emerged in my ColdFusion code recently is the use of the safe navigation operator to easily propagate null and sometimes-null values down through a series of method calls. ColdFusion can handle null values no problem—it just doesn't like null reference errors (NRE), which typically surface in your applications as "key does not exist" errors.

The trick to using the safe navigation operator in method calls is understanding that all values exist in ColdFusion in some sort of scope. This scope is either referenced explicitly in your code; or, ColdFusion will reference a scope implicitly for you if you don't provide one.

If you have a value that might be null in the context of a given scope, you can safely pass a reference to this value around so long as you use the safe navigation operator off of the given scope reference. For example, if you have a variable, result, that might be null within the variables scope, you can pass it into another method as an argument using the safe navigation operator:

anotherMethod( variables?.result )

Internally, this method can then propagate this possibly-null argument onto another method by using the same safe navigation technique. To see this in action, I've created a demo in which sometimes-null values are passed down through two series of method calls: topLevel() and lowLevel(). Ultimately, the lowLevel() method just bubbled back up a stringified version of the arguments which we then subsequently output to the screen.

<cfscript>

	// We're going to be dealing with a value that is SOMETIMES NULL; as such, let's run
	// a number of trials to make sure that we cover both null and non-null flows.
	for ( i in rangeTo( 10 ) ) {

		a = "FirstArg";
		b = "SecondArg";
		c = maybeNullValue(); // Null 50% of the time.

		// Invoke the top-level method with all three arguments knowing that one of them
		// may be NULL in some invocations. We can use the safe navigation operator (?.)
		// to safely reference "c" in its parent scope "variables" without running into a
		// null-reference error (NRE).
		// --
		// Note: all values in ColdFusion (other than the scopes themselves) exist in some
		// scope, whether or not you reference it in your code.
		result = topLevel(
			a = a,
			b = b,
			c = variables?.c // "c" might be null.
		);

		writeOutput( "<p> <strong>Propagated:</strong> #result# </p>" );

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	/**
	* I propagate the arguments down to a lower-level call, adding additional arguments.
	*/
	public string function topLevel(
		required string a,
		required string b,
		string c
		) {

		var d = maybeNullValue(); // Null 50% of the time.

		// Invoke the low-level method with all three incoming arguments plus two
		// additional arguments. Since both "c" and "d" might be null, we can once again
		// use the safe navigation operator to safely propagate them down to the next
		// method call without throwing a null reference error (NRE). Note that we're
		// using the safe navigation operator on two different scopes this time.
		return lowLevel(
			a = a,
			b = b,
			c = arguments?.c, // Incoming "c" might be null.
			d = local?.d,     // Local "d" might be null.
			e = "FifthArg"
		);

	}

	/**
	* I return the stringified list of arguments.
	*/
	public string function lowLevel(
		required string a,
		required string b,
		string c = "(null)",
		string d = "(null)",
		string e = "(null)"
		) {

		return arrayToList( arguments, ", " );

	}

	/**
	* I return a null value 50% of the time.
	*/
	public any function maybeNullValue() {

		if ( randRange( 0, 1, "sha1prng" ) ) {

			return "RealValue";

		}

		return /* NullValue */;

	}

	/**
	* I return a 1...N range, inclusive.
	*/
	public array function rangeTo( required numeric limit ) {

		return arrayNew( 1 )
			.resize( limit )
			.map( ( _, i ) => i )
		;

	}

</cfscript>

This code uses the safe navigation operator when referencing several of the values across several of ColdFusion's scopes: variables, arguments, and local. And, when we run this CFML code, we get the following output:

10 iterations of the demonstration showing a variety of null values mixed in alongside defined arguments.

As you can see, we passed the null values around without any issue because we never referenced a null value. The difference between referencing a null value and passing a null value is subtle but important to understand in ColdFusion. For example, ColdFusion decision functions will happily accept null values; and ColdFusion will happily return null values; and ColdFusion will happily compare null values (including the compare() functions); so long as you never execute a null reference.

And, as a final note, I talked about using the safe navigation operator on "scopes"; but, a scope in ColdFusion is really just a specialized struct. The safe navigation operator can be used on any struct to the same effect.

To Be or Not To Be, That Is the Question

There is much spirited debate about whether or not to explicitly reference scopes in your ColdFusion code. Personally, I omit scopes as much as possible because I believe it makes the code cleaner and easier to read. That said, omitting scopes does have a negligible performance overhead (as ColdFusion maybe have to look in one or two scopes implicitly when searching for your variable reference). Unless you're in a "hot path" of control flow, I think the cleanliness of the code is more important than the performance.

This is fully subjective. I know many very bright people who believe that every reference in your application should be fully scoped. To each their own.

Want to use code from this post? Check out the license.

Reader Comments

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel