The power of ColdFusion functions and closures

The ColdFusion ecosystem has a number of MVC (Model-View-Controller) frameworks — with ColdBox and FW/1 (Framework One) currently being the most popular — and some of them are directly inspired by MVC frameworks in other technologies: cfWheels is heavily inspired by Ruby on Rails, for example. Most of these frameworks offer fairly standard features, regardless of which language they are built with, but some languages are unusual enough that their web frameworks look very different.

I work with Clojure a lot. It’s a general purpose language but it’s a “Lisp for the JVM” which means two things: 1) the parentheses for function calls are on the outside — (my-function 1 2 3) rather than myFunction(1, 2, 3) — and 2) it is a functional programming language first and foremost. The former is something you get used to fairly quickly but the latter can be very hard for someone who is used to regular imperative or object-oriented programming to get their head around. A functional programming language focuses on immutable data (once a variable is initialized, it cannot be changed) and functions — functions for everything. In particular, since your building blocks are functions, not components (like in ColdFusion), it’s common to pass functions as arguments, and return functions as results.

ColdFusion has had this ability since ColdFusion 10 introduced closures and we see it in action with functions like arrayMap(), arrayFilter() and so on, although it’s probably not very widespread as a programming style in our community — at least, not yet. The latest version of FW/1 leverages closures to provide a “builder”-style syntax for rendering REST API results. TestBox leverages closures extensively in its BDD (Behavior-Driven Development) style tests. Here’s an example of a ColdFusion function that accepts two functions as arguments, and returns a new function that “composes” them — wrapping one around the other:

function compose( function f, function g ) {
    return function( any x ) { return f( g( x ) ); };
}
function add1( numeric n ) { return n + 1; }
function times2( numeric n ) { return n * 2; }
calculate = compose( times2, add1 );
calculate( 20 ); // produces 42 by calling times2( add1( 20 ) )

Clojure’s main web framework is called Ring and it does everything with functions. A handler is a function that accepts a struct representing an HTTP request and returns a struct representing an HTTP response, which is not too different from the MVC frameworks mentioned above (although handlers or controllers in our MVC frameworks generally set up data and the framework uses that to render a view which produces the HTTP response). Ring’s power comes from the way it allows you to add functionality to handlers by composing other functions, called “middleware”, around them. A middleware function accepts a handler function and returns a new handler function, just like compose() above does. Authentication, JSON decoding of request data and encoding of response data, error handling, CORS functionality (for OPTIONS in REST APIs), session management, cookie management — all these are done through middleware in Ring.

Working with Ring made me wonder whether ColdFusion was capable of using functions so heavily — could I port Ring to ColdFusion?

Given that this is a “showcase” piece, you should be able to guess that the answer is “Yes!”, so here is Ring for ColdFusion. I’m not suggesting you rush out and start building applications with this — it’s really more a showcase of the power of ColdFusion as a modern language, able to leverage the idioms from functional programming, to produce flexible, composable systems, built on top of small functions. Download it, run it (CommandBox is recommended for that), read through the tutorial (which is a small web application built with Ring), and explore the source code (it’s only a few hundred lines!).

Challenge yourself to leverage the power that recent releases of ColdFusion have unleashed!

Leave a reply

Your email address will not be published. Required fields are marked *

By submitting this form, you accept the Mollom privacy policy.

Related