An Architect's View

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

An Architect's View

Another Closures Example

March 16, 2007 · 4 Comments

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

Tags: coldfusion

4 responses so far ↓

  • 1 Rob Wilkerson // Mar 16, 2007 at 1:31 PM

    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?
  • 2 Sean Corfield // Mar 16, 2007 at 3:11 PM

    @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.
  • 3 Rob Wilkerson // Mar 17, 2007 at 5:38 AM

    @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.
  • 4 Sean Corfield // Mar 17, 2007 at 8:58 AM

    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.

Leave a Comment

Leave this field empty: