My Secret Life as a Spaghetti Coder
home | about | contact | privacy statement
There are plenty of uses for closures, but two of the most useful ones I've found (in general) are when you really want/need to encapsulate something, and when you want to implement the Template Method pattern. Actually, implementing the Template Method pattern using closures may be misusing it, or I may be mischaracterizing it. It's more of like a "generalized" template method (particularly since we're not following the pattern, but implementing the intent). I still don't understand all of the Gang of Four patterns, so have mercy on me if I've gotten it wrong. =) More on all this below.

So as I get ideas from other languages, I always wonder "how can I use that idea in language X." For instance, I like the way Coldfusion handles queries as a data structure - so I tried to implement something similar in Java (which, I will eventually get around to posting). I encountered closures for the first time in Lisp a few years ago in an Artificial Intelligence course, I'm sure. But it wasn't until more recently in my experience with Ruby that I started to understand them. Naturally, I thought "could this be done in Coldfusion?"

Luckily for us, Sean Corfield made a Closures for CF package available a couple of months ago (as far as I can tell). A couple of days ago, I gave it a whirl.

The first thing I have to say is that I had to move the CFCs into the same directory as my closure_test.cfm file because it wasn't able to find the temporary templates it was generating. Sean let me know it was because of a pre CF 7.0 bug in expandPath(), which explains why I had trouble with that function in cfrails. Anyway, I've posted a "fix" for 6.0/6.1 to the comments on his blog, which basically just replaces expandPath(".") when it is creating a temp file to use getDirectoryFromPath(getCurrentTemplatePath()).

After that, it did take a while to understand what was going on, but once I figured it out, it was pretty cool. So here's my first class of using closures, and what I did in Coldfusion to implement it: really "encapsulating" something.

Encapsulation is a word you see thrown around quite often. But you don't achieve it by putting getters and setters everywhere - particularly if you are passing back complex data structures from a get() method (without first making duplicates, of course). The idea behind it, as far as I've been able to understand, is to hide implementation details from code which doesn't need to know it. Not only does this make the client code need to be less complex, it certainly lowers your coupling as well. However, if you have a Container (for instance), how can you encapsulate it? If you return an array representation, you've just let the client code know too much. If you don't let the client code know too much, how will it ever iterate over the contents of your Container?

Certainly an Iterator comes to mind. You could provide that if you wanted to, complete with next(), previous() and anything else you might need. But you could also use a closure to allow you to encapsulate iteration over the Container, while still allowing you the freedom to do what you wanted within the loop. Here's my CF code:

<!--- 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)
      {
         closure.name("run");
         for (i=1; i lte _curIndex; i=i+1)
         {
            closure = closure.bind(value=_arr[i]);
            closure.run();
         }
      }
   </cfscript>
</cfcomponent>

And here's the resulting code that uses Container.each(), and passes a closure to it:

<!--- 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 beenhere = true>");
container.each(c);
c = cf.new("<br/><cfoutput>This container has the value #value# in it</cfoutput>");
container.each(c);
</cfscript>

<cfoutput>
#beenhere# <!--- outputs false --->
</cfoutput>

Now, from my understanding of closures, you should be able to modify "outer" variables within them. Thus, to be a "true" closure, outputting beenhere above should show true. This would be relatively easy to do by sending the appropriate scopes to the closure, but the only ways I can think of to do that would muck up the syntax. Other than that, it is not code that makes me comfortable, so to speak. The closure_test needs to know that it is expected to use value as the name of the variable. There may very well be a simple way around this that doesn't screw up the syntax, but I have yet to spend enough research in figuring it out. In fact, in Ruby you'd use |value| do_something_with(value), so it's probably as simple as that, or providing an extra parameter (as I saw in one of Sean's examples). Perhaps I will when I run across a need for it (and I'm sure I will be using this in the future).

And now for comparison purposes, I've included the Ruby version that does the same thing:

class Container
   def initialize
     @arr = []
     @curIndex=-1
   end

   def add(value)
     @curIndex = @curIndex+1
     @arr[@curIndex] = value
   end

   def each
     cnt = 0
     (@curIndex+1).times do
       yield @arr[cnt]
       cnt += 1
     end
   end
end


#trial run
container = Container.new
container.add 15
container.add 25
container.add 35
beenhere = false
container.each{|v| v+=1; beenhere=true}
puts beenhere #displays true

This isn't normally how you'd code in Ruby, but I tried to keep the look approximately the same. In particular, the each() method is already implemented for arrays in Ruby, so there would certainly be no need to rewrite it.

As you can see, the syntax is a bit cleaner in Ruby than in the CF version. It's so easy to do, one doesn't need to think about using closures - you just do it when it comes naturally. Even with all that, I think Sean reached his goal of proving himself wrong (he said "I was having a discussion about closures with someone recently and I said they wouldn't make sense for ColdFusion because the syntax would be too ugly. On Friday evening, I decided to try to prove myself wrong"). I think it might be possible to improve it a little, but I don't think it would ever become as "pretty" as Ruby's style (unless, of course, it was built into the language). Because of that, and the effort involved in creating them, I don't think it will ever become a natural solution we turn to in CF (although, if I started using them enough, it would eventually become a natural solution). In any case, he certainly did an awesome job, made the syntax much cleaner than I would have thought possible, and gave us a new tool to solve problems with.

In closing, I'd like to talk a bit about the "other" use for closures I've found, what I called a generalized Template Method pattern above. Really, this is just a general case of the way encapsulation was furthered with the each method above. Basically, you are defining the skeleton of an algorithm, and allowing someone else to define its behavior. I mentioned that it's not really the template method pattern, since we aren't confining it to subclasses, and we aren't actually following the pattern - we're just implementing its intent, in a more general way. So basically, you might think of it as a delayed evaluate for letting some other code customize the behavior of your code that expects a closure. I don't know if I've made any sense, but I sure hope so (and if not, let me know what I might clarify!).

Finally I'd like to thank Sean for providing this tool to us, and ask everyone, "what uses have you found for closures, if any?"

Update: Thanks to Brian Rinaldi, Sean Corfield found this post and addressed the problems I encountered. It turns out I was just doing it wrong, so to see it done right, visit his blog. I also responded to a few of the issues raised here.

Hey! Why don't you make your life easier and subscribe to the full post or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!


Comments
Leave a comment

There are no comments for this entry yet.

Leave a comment

Leave this field empty
Your Name
Email (not displayed, more info?)
Website

Comment:

Subcribe to this comment thread
Remember my details
Google
Web CodeOdor.com

Me
Picture of me

Topics
.NET (19)
AI/Machine Learning (14)
Answers To 100 Interview Questions (10)
Bioinformatics (2)
Business (1)
C and Cplusplus (6)
cfrails (22)
ColdFusion (78)
Customer Relations (15)
Databases (3)
DRY (18)
DSLs (11)
Future Tech (5)
Games (5)
Groovy/Grails (8)
Hardware (1)
IDEs (9)
Java (38)
JavaScript (4)
Linux (2)
Lisp (1)
Mac OS (4)
Management (15)
MediaServerX (1)
Miscellany (76)
OOAD (37)
Productivity (11)
Programming (168)
Programming Quotables (9)
Rails (31)
Ruby (67)
Save Your Job (58)
scriptaGulous (4)
Software Development Process (23)
TDD (41)
TDDing xorblog (6)
Tools (5)
Web Development (8)
Windows (1)
With (1)
YAGNI (10)

Resources
Agile Manifesto & Principles
Principles Of OOD
ColdFusion
CFUnit
Ruby
Ruby on Rails
JUnit



RSS 2.0: Full Post | Short Blurb
Subscribe by email:

Delivered by FeedBurner