<cfset cf = createObject("component","closure.ClosureFactory") />
<cffunction name="makePercentCalculator">
<cfargument name="percentage" />
<!---
return a closure that:
- takes a single argument called 'value'
- has the percentage argument bound into it
- is callable as the 'calculate()' method
--->
<cfreturn cf.new("value * p / 100.0","value")
.name("calculate")
.bind(p=arguments.percentage) />
</cffunction>
<!--- create some example calculators --->
<cfset quarter = makePercentCalculator(25) />
<cfset bonus = makePercentCalculator(105) />
<cfoutput>
<p>A quarter of 2345 is #quarter.calculate(2345)#</p>
<p>Your base salary including your bonus is #bonus.calculate(90000)#</p>
</cfoutput>
At this point, quarter is a closure that calculates 25% of its argument - the value 25 was bound to it inside the call to makePercentageCalculator.
I'll continue to post examples until folks start to "get it" - this stuff is hard :)<cffunction name="makePercentCalculator">
<cfargument name="percentage" />
<!---
return a closure that:
- takes a single argument called 'value'
- has the percentage argument bound into it
- is callable as the 'calculate()' method
--->
<cfreturn cf.new("value * p / 100.0","value")
.name("calculate")
.bind(p=arguments.percentage) />
</cffunction>
<!--- create some example calculators --->
<cfset quarter = makePercentCalculator(25) />
<cfset bonus = makePercentCalculator(105) />
<cfoutput>
<p>A quarter of 2345 is #quarter.calculate(2345)#</p>
<p>Your base salary including your bonus is #bonus.calculate(90000)#</p>
</cfoutput>

4 responses so far ↓
1 Rob Wilkerson // Mar 16, 2007 at 1:31 PM
This example is like the JS examples I've seen and, although I can see what the code is doing, I'm missing that epiphany that explains how it's better (or easier, or whatever adjective applies here) than simply having a single function where two arguments are passed in:
function getPercentage ( value, percentage ) {
return value * percentage / 100;
}
Does that make sense?
2 Sean Corfield // Mar 16, 2007 at 3:11 PM
Essentially the closure is an object with a method and some data bound into it. The way you create the closure (object) from the code fragment is the piece that makes it slightly different - you can just provide a string.
3 Rob Wilkerson // Mar 17, 2007 at 5:38 AM
Okay, I really do get the theory (finally), I guess in my experience I've never come across a time when that kind of coupling seemed so tightly coupled and an additional layer of abstraction was necessary. Can you offer such a scenario and maybe explain why that extra abstraction would be helpful?
Thanks for addressing this concept for the uninformed masses. I think I'll get a lot out of it when I have that epiphany moment.
4 Sean Corfield // Mar 17, 2007 at 8:58 AM
For example, you might have a module that need to calculate tax on products. It doesn't need to know *how* to calculate the tax, it just needs a calculator. Whatever calls the module can pass in the calculator function / object / closure. Tax might be a flat rate, might be zero under some circumstances or might be tiered (lower tax on more expensive values).
Closures are a convenient way to create functions on the fly, with bound variables. You don't *need* them - you can always hand-write a class and instantiate it and pass in the variables you want to bind in place etc. Which is, of course, how my closures library works behind the scenes.
Leave a Comment