Skip to main content
Ben Nadel at Scotch On The Rocks (SOTR) 2011 (Edinburgh) with: Steven Erat
Ben Nadel at Scotch On The Rocks (SOTR) 2011 (Edinburgh) with: Steven Erat

CachedWithin Function Memoization Can Be Applied To Closures In Lucee 5.3.2.77

By
Published in Comments (2)

Over the past couple of weeks, I've written a few posts about the cachedWithin Function memoization feature in Lucee 5.3.2.77. I've looked at how it compares complex inputs by value; and, at how it returns complex outputs by value. But, all of my explorations to date have been on a "proper" Functions. This morning, I wanted to quickly demonstrate that the cachedWithin Function memoization feature can also be applied to ColdFusion Closures in Lucee 5.3.2.77.

In order to add caching to a Function in Lucee CFML, all we have to do is add the cachedWithin directive to the Function declaration. This applies to ColdFusion Closures as well. Which means, we can create a caching version of any User Defined Function (UDF) by simply wrapping it inside of a Closure that contains a cachedWithin directive.

ASIDE: This only pertains to user defined functions (UDF) since native ColdFusion functions, like ucase, cannot be passed-around like first-class citizens. Built-in functions are a fundamentally different "Type" of object.

To see this in action, I've created a UDF that returns a random integer. Then, I pass that UDF to a memoization function that wraps it in a cachedWithin closure:

<cfscript>

	/**
	* I return a random integer between 1 and the given max value (inclusive).
	* 
	* @maxValue I am the optional max value of the range.
	*/
	public numeric function getRandomValue( numeric maxValue = 10 ) {

		return( randRange( 1, maxValue ) );

	}


	/**
	* I create a version of the given callback that uses cached responses for the
	* duration of the request.
	* 
	* @callback I am the Function being memoized.
	*/
	public any function memoizeForRequest( required any callback ) {

		// NOTE: We are applying the CachedWithin directive to the Closure definition.
		// This creates a caching proxy for the underlying callback.
		// --
		// NOTE: When adding meta-data for a Closure, you cannot use the Fat-Arrow
		// notation - you have to use the standard "function" syntax.
		return(
			function() cachedWithin = "request" {

				return( callback( argumentCollection = arguments ) );

			}
		);

	}

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

	// Test the base, non-caching function.
	echo( "<h2> Using Base Function </h2>" );
	echo( "#getRandomValue()# <br />" );
	echo( "#getRandomValue()# <br />" );
	echo( "#getRandomValue()# <br />" );
	echo( "#getRandomValue()# <br />" );
	echo( "#getRandomValue()# <br />" );
	echo( "<hr />" );

	// Now, let's wrap the base function in a caching proxy.
	cahedRandomValue = memoizeForRequest( getRandomValue );

	// Because this is the caching version, the following calls should all results in the
	// same output (from using the same input).
	echo( "<h2> Using Caching Closure </h2>" );
	echo( "#cahedRandomValue( 50 )# <br />" );
	echo( "#cahedRandomValue( 50 )# <br />" );
	echo( "#cahedRandomValue( 50 )# <br />" );
	echo( "#cahedRandomValue( 50 )# <br />" );
	echo( "#cahedRandomValue( 50 )# <br />" );

	// And, one final test to demonstrate that cachedWithin is based on inputs.
	echo( "#cahedRandomValue( 100 )# <br />" );

</cfscript>

As you can see, we're passing the getRandomValue() function into the memoizeForRequest() Function, which returns a caching-proxy to the original Function. Subsequent calls to this caching proxy should all result in the same output, assuming that the calls use the same inputs (arguments). And, when we run this CFML code, we get the following output:

The cachedWithin direcitve applied to a Closure caches the results of the underlying Function call in Lucee 5.3.2.77.

As you can see, when the cachedWithin Closure is invoked with no arguments, the same "random" value is returned 5-times in a row. This is because the first call to the proxy Closure cached the results of the underlying call to getRandomValue(); and then, each subsequent invocation of the Closure returned the cached value.

Of course, once we change the inputs to the caching proxy, the cachedWithin directive makes another call to the underlying getRandomValue() function, caching the new result.

Off the top of my head, I don't have a great use-case for this yet. But, it's good to know that the cachedWithin Function memoization feature of Lucee CFML 5.3.2.77 can be applied to ColdFusion Closures as well as normal User Defined Functions (UDF). This means that we can easily create on-the-fly caching for any Function call without having to explicitly manage a in-memory cache.

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

Reader Comments

226 Comments

I am really loving your exploration of these topics...and am following along. Super appreciate it. Also, really appreciate that you've begun providing video overviews again, making it much more likely that I follow along. And I enjoy hearing you talk through your thought process as well. ???? (<--prayer hands)

15,811 Comments

@Chris,

Awesome my man, I am glad you are enjoying. It's fun to find all these little use-cases. And, I'm also happy to make the videos. I've historically been lazy about making them for server-side stuff. But, I do like the ability to paint a different picture - give it some different coloring and some more back-story. Definitely a value-add.

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