Skip to main content
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: RC Maples
Ben Nadel at InVision In Real Life (IRL) 2019 (Phoenix, AZ) with: RC Maples

Using Lambda Functions To Short-Circuit Control Flow In ColdFusion

By
Published in Comments (1)

Whenever possible, I prefer guard statements to deeply nested code. Guard statements rely on short-circuiting mechanics instead of conditional execution. Which, for me, makes guard statements much easier to read, reason about, and maintain.

But, in some contexts, short-circuiting out of the control-flow is problematic. Consider the processing of a form submission that has to orchestrate multiple database operations. If one of the operations fails, you need to re-render the form and display error information. Which means that you likely need to invoke some post-processing / pre-view logic. Which means that short-circuiting at a top-level will skip over code that still needs to execute.

Consider this mock controller that's saving three related data models:

<cfscript>

	formData = {};

	// Process form submission.
	if ( isPost() ) {

		if ( saveRouter() ) {

			if ( saveProductAssociation() ) {

				if ( saveNote() ) {

					// All the models saved successfully, we can forward to next page.
					redirectTo( action = "show" );

				}

			}

		}

	}

	// Setup any view-related data that doesn't need to be done if form was successfully
	// processed.
	breadcrumbs = [ {}, {}, {} ];
	statusOptions = [ {}, {}, {} ];

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

	// Mock functions to help illustrate Model interactions.
	function isPost() { return true; }
	function saveRouter() { return true; }
	function saveProductAssociation() { return true; }
	function saveNote() { return true; }
	function redirectTo() { writeOutput( "Redirecting..." ); }

</cfscript>

As you can see, I have response-related data being initialized at the bottom of the control flow (breadcrumbs and options). As such, I can't short-circuit out of the controller execution or I'll end up with null reference errors when my "view" and "layout" templates render.

In this case, we can use an Immediately Invoked Function Expression (IIFY) to wrap a portion of the control flow in a Function body. This gives us the ability to short-circuit just that portion of the control flow without having to short-circuit the entire controller execution.

<cfscript>

	formData = {};

	// Process form submission.
	if ( isPost() ) {

		// This immediately invoked function expression (IIFY) gives us a quarantined
		// control-flow from which we can short-circuit the local execution without short-
		// circuiting the overall execution of the template.
		(() => {

			if ( ! saveRouter() ) {

				return;

			}

			if ( ! saveProductAssociation() ) {

				return;

			}

			if ( ! saveNote() ) {

				return;

			}

			// All the models saved successfully, we can forward to next page.
			redirectTo( action = "show" );

		})();

	}

	// Setup any view-related data that doesn't need to be done if form was successfully
	// processed.
	breadcrumbs = [ {}, {}, {} ];
	statusOptions = [ {}, {}, {} ];

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

	// Mock functions to help illustrate Model interactions.
	function isPost() { return true; }
	function saveRouter() { return true; }
	function saveProductAssociation() { return true; }
	function saveNote() { return true; }
	function redirectTo() { writeOutput( "Redirecting..." ); }

</cfscript>

Now, when one of the operations fails, we can return out of the IIFY and pick-up execution after the Function block. This allows us to short-circuit the quarantined block of code without having to short-circuit the rest of the template.

The IIFY provides ColdFusion with a way to apply short-circuiting mechanics to an isolated block of code. It also helps illustrate how portions of the code might be factored-out into their own methods.

Many Ways To Refactor This Code

There are many ways that one could refactor this control-flow, such as moving the orchestrated database operations out into their own Function or even their own Model. And, in fact, that's what I would likely do in the long run.

But, the point of this post isn't to illustrate application architecture — it's to illustrate code mechanics. And, to demonstrate how one can improve the readability of the code (with guard statements) when a larger refactoring isn't possible or isn't warranted.

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

Reader Comments

16,125 Comments

In my post, I very much wanted to declare that "Guard Statements" are objectively better than nested control flow. But, as I get older, I'm realizing that more and more and more of just about everything is deeply subjective. And, I'm sure there will be people that read this and think that the nested control-flow logic is easier to read.

A younger and dumber me would just think that those people are wrong 🤪 but the older more mature me doesn't want to yuck anyone's yum. That said, I thought of an interesting parallel in JavaScript.

In the early JavaScript era, deeply nested control flow was often referred to a "Callback Hell" (a byproduct of Node's asynchronous I/O mechanics). Then, when JavaScript introduced the async/await syntax, the readability of JavaScript improved dramatically because it — in part — allowed JavaScript developers to use guard statements even with asynchronous operations.

And the world rejoiced!

I think most JavaScript developers would agree that async/await is vastly easier to read and maintain than the nested callback logic. In that vein, I would say that the pattern of short-circuiting is generally easier to read and maintain than nested conditional logic.

I don't think this thought adds much to the conversation; but, I liked the parallel, so I figured it would be best articulated in a comment.

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
Managed hosting services provided by:
xByte Cloud Logo