An Architect's View

CFML, Clojure, Software Design, Frameworks and more...

An Architect's View

Sammy Larbi on Closures

January 2, 2007 ·

Thanx to Brian Rinaldi's Open Source Update, I found a new blog - Sammy Larbi's. Sammy tried out my Closures library and shows an example that sort of works so I figured I'd post the "right" way to do things so that a variant of Sammy's example works as he expects. First off, Sammy mentions the fix he had to apply to make the Closures library work on his system. I thought it was a 6.1 / 7.0 difference but it turns out he found a real bug and the fix is needed even on 7.0 if you use a mapping for the library. Secondly, while debugging Sammy's example, I found another bug - a potential thread safety issue. Both are fixed in SVN and the latest "daily" build ZIP file. So, moving on to Sammy's code, here's the corrected version of his container.cfc:
<!--- container.cfc --->
<cfcomponent>
<cfscript>
variables._arr = arrayNew(1);
variables._curIndex = 0;

// adds an element to the container
function add(value)
{
_curIndex = _curIndex + 1;
_arr[_curIndex]=value;
}

// iterates over the container, letting a closure
// specify what to do at each iteration
function each(closure)
{
for (i=1; i lte _curIndex; i=i+1)
{
closure.call(_arr[i]);
}
}
</cfscript>
</cfcomponent>
Compare his each() method to mine. He names the method and then repeatedly binds the value variable. That doesn't actually do what he thinks. If you want to call a closure, you simply call it with arguments as shown above. However, in order to do that, you need to identify the arguments when you create the closure. You do that by specifying the argument names in the call to new() like this:
cf.new("<cfset value = value + 3>" &
   "<cfoutput>##value##</cfoutput>" &
   "<cfset beenhere = true>","value")
That fixes part of his problem. His other problem is the beenhere variable. He wants to bind that variable to the context in which the closure is created. Note that he wants to update the variable inside the closure and output its (updated) value outside the closure. In order to do that, you need to bind the context. First we change the closure to this:
cf.new("<cfset value = value + 3>" &
   "<cfoutput>##value##</cfoutput>" &
   "<cfset outer.beenhere = true>","value")
We've made beenhere a member of a structure. Now we can bind the variables context of the closure_test.cfm page to that structure:
cf.new("<cfset value = value + 3>" &
   "<cfoutput>##value##</cfoutput>" &
   "<cfset outer.beenhere = true>","value")
   .bind(outer=variables)
This causes the (free) reference to outer in the closure to be bound to the (creation-time) variables scope context. Here's the updated test page:
<!--- closure_test.cfm --->
<cfscript>
cf = createObject("component","org.corfield.closure.ClosureFactory");
container = createObject("component","container");
container.add(10);
container.add(20);
container.add(30);
beenhere = false;
c = cf.new("<cfset value = value + 3>" &
   "<cfoutput>##value##</cfoutput>" &
   "<cfset outer.beenhere = true>","value")
   .bind(outer=variables);
container.each(c);
c = cf.new("<br/><cfoutput>This container has the value ##value## in it</cfoutput>","value");
container.each(c);
</cfscript>

<cfoutput>
#beenhere# <!--- outputs false --->
</cfoutput>
Hope that helps anyone else who is trying to use my Closures library!

Tags: coldfusion · oss

0 responses