<cfset doubler = cf.new("multiplier * arguments.n").bind(multiplier=2) />
<p>doubler.call(n=21) = #doubler.call(n=21)#</p>
<cfset calc = cf.new("multiplier * n","n") />
<cfset calc.name("do") />
<cfset triple = calc.bind(multiplier=3) />
<p>triple.do(14) = #triple.do(14)#</p>
You'll get 42 each time. No idea what anyone might want to use this for but it's "cool". Enjoy!
<p>doubler.call(n=21) = #doubler.call(n=21)#</p>
<cfset calc = cf.new("multiplier * n","n") />
<cfset calc.name("do") />
<cfset triple = calc.bind(multiplier=3) />
<p>triple.do(14) = #triple.do(14)#</p>
Peter Bell added a few comments including a link to Martin Fowler talking about closures. I figured I'd add an example showing how you might do similar things in ColdFusion with this library. First off, you need a select function to match Ruby:
<cfscript>
function select(collection,closure) {
var result = structNew();
var e = 0;
for (e in collection) {
if (closure.call(collection[e])) {
result[e] = collection[e];
}
}
return result;
}
</cfscript>
Now we'll build a collection (a struct) containing some simple struct data elements:
function select(collection,closure) {
var result = structNew();
var e = 0;
for (e in collection) {
if (closure.call(collection[e])) {
result[e] = collection[e];
}
}
return result;
}
</cfscript>
<cfset emps = structNew() />
<cfset e = structNew() />
<cfset e.name = "Sean" />
<cfset e.isManager = true />
<cfset emps[e.name] = e />
<cfset e = structNew() />
<cfset e.name = "Matias" />
<cfset e.isManager = false />
<cfset emps[e.name] = e />
<cfset e = structNew() />
<cfset e.name = "Paul" />
<cfset e.isManager = false />
<cfset emps[e.name] = e />
Finally, we run the Ruby-like select method:
<cfset e = structNew() />
<cfset e.name = "Sean" />
<cfset e.isManager = true />
<cfset emps[e.name] = e />
<cfset e = structNew() />
<cfset e.name = "Matias" />
<cfset e.isManager = false />
<cfset emps[e.name] = e />
<cfset e = structNew() />
<cfset e.name = "Paul" />
<cfset e.isManager = false />
<cfset emps[e.name] = e />
<cfset mgrs = select(emps,cf.new("e.isManager","e")) />
We create the closure to match Ruby's code block { |e| e.isManager } - we name the argument (the second argument to the new() method so it can be called without having to used named arguments - as the select() method expects.

19 responses so far ↓
1 Jared Rypka-Hauer // Oct 8, 2006 at 4:54 PM
What for, other than cool? Maybe Peter Bell can use this for his application generation framework. ;)
JUST TEASING PETER! :D
J
2 Peter Bell // Oct 8, 2006 at 9:23 PM
@Jared - NP at all! I've been reading up on closures on and off for weeks now. Now at least I have some sample CF code so if I can just think of something to use this for I'll be set!
Truth be told, I still don't really "get" closures, but I have a book on Haskell with my name on it and I'm reading a lot of Ruby code, so I'll be sure to let you know if I figure out what this could be used for.
Like Mixins, I'm sure there's gold in tham thar patterns . . .
@everyone - if anyone thinks up some interesting use cases for closures in CF I'd love to hear them!
3 Ken Dunnington // Oct 9, 2006 at 6:50 AM
4 Peter Bell // Oct 9, 2006 at 7:08 AM
http://www.martinfowler.com/bliki/Closure.html
5 Sean Corfield // Oct 9, 2006 at 8:17 AM
The Ruby examples Fowler gives assume collections of objects and a select function. Select takes a closure and returns the elements of the collection for which applying the closure's method returns true.
I'll edit my entry to show how that might work in ColdFusion with this closure library.
6 Peter Bell // Oct 9, 2006 at 8:22 AM
7 Barney // Oct 9, 2006 at 8:53 AM
function select(collection, closure) {
var result = structNew();
var e = "";
for (e in collection) {
if (closure(collection[e])) {
result[e] = collection[e];
}
}
return result;
}
function test(e) {
return e.isManager;
}
select(emps, test);
The only thing missing is the function literal in the call to select (you have to predefine it). If CF would just support functions as a "real" datatype (including a literal syntax), then you'd have it all, without any extra weirdness.
Of course, you'd want the scoping rules of a closure as well, but I assume that your CFC's don't provide that? It would be remarkably inefficient to do so, I'd think.
8 Sean Corfield // Oct 9, 2006 at 9:40 AM
cf.new("return e.empStatus = statValue;","e").bind(statValue=x)
That creates a closure with statValue bound to the local x variable in the calling code...
9 Barney // Oct 9, 2006 at 11:45 AM
Which isn't to say this isn't useful, of course, just that there are some gotchas to be aware of.
10 Sean Corfield // Oct 9, 2006 at 11:59 AM
Currently, when you bind a value type, the closure keeps that value (even if the variable is subsequently changed); when you bind a reference type, the closure keeps the reference but picks up live changes. Also, the closure itself has "history" in that changes to bound variables inside the closure affect future calls to the same closure instance (this is actually useful since you can write an aggregator with a closure).
That may indeed cause surprises. I considered machinery to perform a deep copy of bound variables but decided it was too much overhead for a proof of concept.
I'll be interested to see what sort of feedback people have once they start trying to use this in the wild...
11 Peter Bell // Oct 9, 2006 at 1:31 PM
Could you suggest some starting points (as with mixins which now make all the sense in the world to me) for experimentation?
If you're building a cms, writing a DAO or service or controller, creating a shopping cart or write a newsletter system where is a time that a closure would help you to solve a problem more flexibly or elegantly than just having methods within a class.
About the only thing that has penetrated for me is that you can dynamically create a function that has certain cool properties in terms of its existance and relationship to or access to the caller scope or something. Any thoughts appreciated, but more importantly, I'm voting for this to be a project if you have the time and will definitely take the time to learn enough about closures to take advantage of anything you create!
12 Mark Mandel // Oct 9, 2006 at 5:53 PM
That way it could be very easy to filter out objects from a group, or resort them in a custom way that Transfer doesn't automatically handle internally.
13 Sean Corfield // Oct 9, 2006 at 7:53 PM
I'd have to look through my old ColdFusion code to see if I can come up with some examples that would be made simpler by the use of closures.
Mark may be on the ball with using closures to create reusable ad hoc queries... definitely a way to create filters etc on the fly.
14 Tom Chiverton // Oct 10, 2006 at 1:44 AM
This has implications for someone else coming along and looking at your code.
15 Sean Corfield // Oct 10, 2006 at 7:51 AM
16 Peter Bell // Oct 10, 2006 at 8:04 AM
ANY new pattern requires a learning curve on the part of other developers reading your code. Think of how hard it is for a procedural CF developer to make sense of a well architected application using CFCs. Does that mean CFC's are wrong?!
The question is whether the additional learning pays for itself by allowing you to solve problems more elegantly. For en example, look at my LightWire code as a good example of Object based mixins. You have an entire DI/IoC engine including the resolution of circular dependencies in under 200 lines of code (including a couple of required base methods). In many ways it is an inferior approach when compared to ColdSpring, but ColdSpring is a little more than 200 lines of code! Using object based mixins allows DI to be simplified to the point where anyone can "get it" and indeed anyone can just "write it themselves" if they have a good reason to.
Mixins can be confusing when you first see them, but they may repay the study if you have the kind of problem they can be used for (despite all of the usual caveats about losing the self documenting nature of the class files).
I'm pretty sure closures are the same and am interested in Marks ideas above and to hear if anything concrete comes to Sean.
17 Tom Chiverton // Oct 11, 2006 at 1:55 AM
@Peter: I'm not against learning something new, if someone can make the case it is worthwhile (CFCs over UDF for instance). I'm not seeing closures as offering anything. Maybe as Sean said it was a bad example.
18 Sammy Larbi // Dec 16, 2006 at 2:01 PM
19 Sean Corfield // Dec 16, 2006 at 5:47 PM
Glad to hear you got it working.
Leave a Comment