Skip to main content
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Richie Beamish
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with: Richie Beamish

Using Constants To Help Clarify Boolean Arguments And Return Values In ColdFusion And JavaScript

By
Published in , Comments (2)

A few weeks ago, I talked about how I often see some very questionable Boolean arguments in old, legacy code. This has kept Boolean arguments top-of-mind for me; and one thing that I've started to experiment with recently is the use of "Constants" - named, static values - to bring additional clarity to function invocation expressions that require a Boolean flag. This can be done in both ColdFusion / Lucee CFML as well as JavaScript - my two main languages. And, I wanted to share a quick demo.

This whole idea is actually inspired by something that Kyle Simpson said in Functional Light, his book about Functional programming (FP). In the book, Kyle talked about the criticality of name selection; and, how the name filter() is far too ambiguous - that the function should have been called something like filterIn() or filterOut() so that the semantics of the Function's return value would have high clarity.

To that end, I thought to myself, can I just return a "constant" variable whose name adds that same kind of clarity? To see what I mean, take a look at this snippet of ColdFusion code which exercises a few functions that accept a Boolean flag:

<cfscript>

	FILTER_IN = true;
	FILTER_OUT = false;
	CREATE_PATH = true;
	RECURSE_DIRECTORIES = true;

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

	friends = [ "Sarah", "Todd", "Kim", "Samantha", "Danny" ].filter(
		( friend ) => {

			if ( friend.left( 1 ) == "S" ) {

				return( FILTER_IN );

			} else {

				return( FILTER_OUT );

			}

		}
	);

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

	directoryCreate( expandPath( "./foo/bar/baz" ), CREATE_PATH );
	directoryDelete( expandPath( "./foo" ), RECURSE_DIRECTORIES );

</cfscript>

Here, I'm using the .filter() member-method and the directoryCreate() and directoryDelete() functions, each of which uses a Boolean flag in some fashion. The .filter() method uses the Boolean (more specifically a "Truthy") return value; and, the directory functions take Boolean arguments. And, in each case, instead of passing or returning a literal true, I'm returning a constant that reference true, but does so with an additional name-based affordance.

Now, I know what you're thinking - because, I'm thinking it too: why not just rewrite the "filter" invocation to be much more simple. Something to the effect of:

<cfscript>

	// ....
	.filter(
		( friend ) => {

			return( friend.left( 1 ) == "S" );

		}
	)
	// ....

</cfscript>

... and I agree with you. In fact, for such a trivial case, this is typically the way that I would write it.

ASIDE: Some of you may even think that such a simple filter should be rewritten with an even more concise syntax:

.filter( ( friend ) => friend.left( 1 ) == "S" )

But, this is where we disagree. The whole point of this article is to think about clarity of code; and, such extreme short-hand notation is the antithesis of clarity. It's fine for "code Golf"; but, it should not be considered for code that other people may have to maintain one day.

But, sometimes, a .filter() function requires a lot more logic than a simple comparison. And, in those cases, I'm finding the named constants to be quite luxurious. To see what I mean, here's a snippet of real-world JavaScript from a feature that I'm currently writing at InVision:

var FILTER_IN = true;
var FILTER_OUT = false;

// .... truncated code ....

// STEP 3: Now that we've populated our node-link collections, let's remove
// any circular references that we created. We want our tree to only flow in
// one direction (we can represent circular links in the UI in alternative
// ways). To do this, we're going to start from the ROOT NODE and then
// perform a BREADTH-FIRST TRAVERSAL of the links, deleting links to nodes
// that we've already visited.
var nodesToVisit = [ nodesIndex[ rootScreenID ] ];
var nodesToVisitIndex = _.indexBy( nodesToVisit, "id" );
var unvisitedNodesIndex = _.assign( Object.create( null ), nodesIndex );

// Keep traversing while we have unvisited nodes in our queue.
while ( nodesToVisit[ 0 ] ) {

	// Remove this node from the nodes that are pending inspection.
	var node = nodesToVisit.shift();
	delete( nodesToVisitIndex[ node.id ] );
	delete( unvisitedNodesIndex[ node.id ] );

	// Traverse linked nodes AND FILTER OUT the ones that we've already
	// visited (or are already planning to visit).
	node.links = node.links.filter(
		function operator( linkedNode ) {

			// If the linked node has already been visited (in that it is no
			// longer flagged as unvisited), then exclude it.
			if ( ! unvisitedNodesIndex[ linkedNode.id ] ) {

				return( FILTER_OUT );

			}

			// If the linked node has been queued-up for visitation but has
			// not yet been visited, then exclude it.
			if ( nodesToVisitIndex[ linkedNode.id ] ) {

				return( FILTER_OUT );

			}

			// At this point, we know that we haven't seen this linked node
			// before, queue it up for BREADTH-FIRST traversal.
			nodesToVisit.push( linkedNode );
			nodesToVisitIndex[ linkedNode.id ] = true;
			return( FILTER_IN );

		}
	);

} // END: While-loop.

Here, the .filter() function is much more intense than your average value-based filtering. In fact, just trying to understand how this breadth-first tree traversal works has a significant amount of cognitive-load. As such, having to also understand what a true or false return value means seems cruel. But, when those raw values get replaced with the named constants FILTER_IN and FILTER_OUT, I feel like it really frees up my brain to focus on the algorithm itself rather than the function semantics.

To be clear, I'm not saying that I'm changing all of my Boolean flags to use named constants. Sometimes, code is sufficiently simple and doesn't require any additional brain-hacks to be readable. However, when things start to get a bit hairy, I'm really enjoying how much additional clarity a meaningfully named constant can bring to the code.

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

Reader Comments

198 Comments

Personally, I think this would be overly verbose. The code comments already explain what it does. If you wanted more inline clarity you could always do something like:

return true /* FILTER_IN */;

or

return true /* INCLUDE */;

I just think the code is going to end up getting littered with Boolean constants over time, unless all your code is broken up into micro fragments, then you're going to be looking for ways to manage all the constants, then you've just added more code and more potential bugs.

15,848 Comments

@Dan,

That's fair - and, I've used the inline-comments in the past for more clarity. Especially on the directoryX methods, like:

directoryCreate( path, true ); // True = create path.
directoryDelete( path, true ); // True = recurse directories.

I think that's a totally decent approach.

As far as "managing" constants, I definitely wouldn't try to do anything "clever" or "DRY" with them - I just copy them as needed where they are needed. Meaning, I wouldn't try to have a centralized list of constants - that's not the value-add.

But, like I said, this is just something I've been playing with - not necessarily something I'm sold on using all the time.

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