CF9 + E4X + C4X

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

Published by

Rick Osborne

I am a web geek who has been doing this sort of thing entirely too long. I rant, I muse, I whine. That is, I am not at all atypical for my breed.

4 thoughts on “CF9 + E4X + C4X”

  1. I’m right with you on the E4X-style (C4X) solution, I think it’s the one that makes the most sense and probably the easiest to implement. Declaring XML literals would be the icing on the cake :)

    Re: your last paragraph – perhaps any language will be able to do that kind of thing soon with some of the most useful services being exposed in CF9. Meanwhile, CFML definitely has it’s place.

  2. While I think that e4x would be a great idea, I’m kind of stuck on what the implementation would look like. The part that gets me is where the return variable gets set. In your cfquery example, the cfquery tag actually returns something, but in regular tag based cf, the variable gets set as an attribute in the tag. If I were to add a name attribute to the e4x version what should happen? Should it create another variable or throw an error? Or should the c4x cfquery work differently than the regular cfquery. This also makes cfmail more interesting as well since it doesn’t return anything.

  3. Adding a name attribute to the cfquery tag would be redundant, so it would probably be best to throw an attribute validation error (something like “Name attribute not supported in script”) and have the query object assigned to the variable only. For something like cfmail the return value doesn’t matter (since there isn’t one), but the variable assignment would be required for the syntax to be valid. I think it should be pretty straight forward.

  4. @Justin, that’s why it would be confusing. The tags and usage in cfscript would be different than the tags and usage outside of cfscript.

    What would be the point of cfmail returning a variable? I can only see this being useful if mail isn’t spooled, so booleans can be returned depending on the successful sending of an email.

Comments are closed.