Viewing By Entry / Main
March 16, 2007
This is a nice, simple example that shows binding of 'free' variables.

There is a 'factory' method (makePercentageCalculator) that takes a percentage and returns a closure that calculates that percentage of any argument passed to it:

<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 :)

Comments

Here's the thing: I get it, I just don't *get* it. :-)

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?


@Rob, because the code that calls the calculator method might not have access to the percentage (and should not need to be coupled to).

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.


@Sean: 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.


Well, the abstraction is never actually *necessary* but it's just a way of improving the maintainability of the system by decoupling things so that they can be modified independently.

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.


Post Your Comments
Name:
Email Address:
Comments
*** Please note that all comments require moderation so it may be some time before your comment posts to this blog! ***
Remember My Information:
 



Hosting provided by