CF9 + E4X + C4X

May 19

Sean Corfield called for comments about how to embed tag collections into cfscript. I proposed an E4X-style solution, where chunks of old-style CF code could be embedded without any conversion to objects or other flourishes.

What is it that makes ColdFusion into ColdFusion? For me, it’s that it’s not tied down into boring object-imperative code:

var m = new cfmail();
m.from    = "whatever";
m.to      = "whoever";
m.subject = "however";
m.body    = "blah blah blah";
m.send();

Ugh. That kind of blocky syntax makes it look and feel like ASP. If I wanted to code for ASP, I’d code for ASP. (And get a lobotomy.)

The beauty and elegance of ColdFusion is its declarative nature. I don’t create an email object or new() up a query object, I just declare that I want an email or a query. Done. I think declarative E4X-style literals—turning XML and CF tags into primitives like strings and numbers—keeps the “just give it to me” feel in CF.

CF was built for moving data between web pages and databases. Upgrading XML fragments to first-class primitives fits with that theme. Once you’ve got that in your head, it’s not hard to see CF code fragments as primitives, too.

Here’s a more detailed example of the kind of cfscript I’d love to see:

import org.rickosborne.util.*;
component extends="CachedObject" {
    public query queryUserByID( id, userType = "" ) {
        // Return a query of the user information
        var ret = cachePeek(id);
        if(isQuery(ret) and (ret.recordCount gt 0)) return ret;
        ret = <cfquery datasource="#this.dsn#">
          SELECT *
          FROM users
          WHERE (id = <cfqueryparam value="#arguments.id#" cfsqltype="CF_SQL_INTEGER">)
            <cfif structKeyExists(arguments, "userType") and (arguments.userType neq "")>
            AND (type = <cfqueryparam value="#arguments.type#" cfsqltype="CF_SQL_VARCHAR">)
            </cfif>
          </cfquery>;
        if(ret.recordCount gt 0) cachePoke(id, ret);
        return ret;
    } // getUserByID
    public xml xmlUserByID( id ) {
        // Return an XML representation of user data
        // which can then be transformed with XSLT
        var ret = <user />;
        var user = queryUserByID(arguments.id);
        var colName = "";
        if(!(isQuery(user) and (user.recordCount eq 1))) return ret;
        for(colName in listToArray(user.columnList)) {
            ret.appendChild(<#lcase(colName)#>#xmlFormat(user[colName][1])#</#lcase(colName)#>);
            // or maybe ...
            ret.childNodes += <#lcase(colName)#>#xmlFormat(user[colName][1])#</#lcase(colName)#>;
            // or if that's too hard on the parser, go the long way around
            ret.appendChild(xmlParse("<#lcase(colName)#>#xmlFormat(user[colName][1])#</#lcase(colName)#>"));
        } // for colName
        return ret;
    } // formatUser
} // component

(But, honestly, at that point why start with a ColdFusion engine? Why not just start with a JavaScript/EcmaScript engine such as Rhino, then just extend it to support CF functions and objects?)