CachedWithin Function Memoization Can Be Applied To Closures In Lucee 5.3.2.77
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:
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
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)
@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.