Wednesday, August 10, 2011

Dear ColdFusion Team: Please fix this stuff in Zeus

I love ColdFusion. I've built a 13 year career out of it (so far). I want to see CF continue to be a great language. Here are a few things I see that need to be addressed:

1. Shorthand for creating structures
This is one of those things that I'm not sure how it got put into the language, but lets fix it now before it becomes one of those embedded issues we just have to accept for version after version like 1 based array indices (more on that later). Fix it now!

// current
myStruct = {key="value"};

// suggested
myStruct = {key:"value"};

2. Arrays should be zero-indexed
This is one I've mentioned on for years. The standard across every other language is to use zero based indices. Why CF didn't do this from the get-go is an enigma to me. It is a PITA and requires a different sort of looping criteria as well as a different sort of index allocation during creation (unless you are using arrayAppend()). I would like to suggest making this backwards compatible by offering an application level variable that will permit you to enable the old 1 based indices while you are converting apps or maintaining legacy apps.

3. Looping over Arrays (item versus index)
This is a biggie that was discussed on other blogs recently. When I loop over an array in tags I do this:
<cfloop array="#myArray#" index="item">

This makes no sense. An index is just that, the current array index you are looking at. In CF this actually returns the item specified by the current index. I cannot get the current index without searching my array. The correct way to implement this would be to allow the user to specify either attribute. This would also prevent the user from having to track the index via an incrementer. I have had to do this frequently when trying to match up the current array with another array with related data.

<cfloop aray="#myArray#" index="i" item="item">

In this loop index would refer to the current index, and item would refer to the current item - makes sense right?

4. Looping over queries in CFScript
The way to do this currently is to loop from 1 to qry.recordCount. This isn't terrible, however I'd like to see this updated as such:
for ( row in myQuery ) {
}

The same could be said for an array loop.
for ( item in myArray ) {
}

5. Clean up object functions
Let's catch up with other languages on this one. len(), arrayLen() etc. Instead, I should be able to use a variable.length function regardless of the type (string or array). Also arrayAppend() (myArray.push()), arrayGetAt() (myArray.getAt(), myArray.pop()), listAppend() (myList.addItem()), etc. There are tons of them that should be addressed. You don't need to do anything but deprecate these existing functions for a few versions.

6. Query() object
Well, this one is tricky, as I've been trying to come up with a better way of handling this myself. Going back to the early days of ColdFusion (back when we called it Cold Fusion ;) ) the initial selling point of the language was the ability to interact easily with databases. This was so much the case that IIRC the tag was actually prefaced db and not cf! is a very simple way to access queries, and I continue to use it as opposed to its script counterpart. In fact, this is so much the case that I have to write my DAOs in tag based syntax instead of using script, which would be preferred.

I'm not going to go into all the reasons I dislike the current script based query syntax, but my biggest issue currently is with how query params are assigned. I'd recommend this approach:

var myQuery = new Query({
sql: "SELECT col from table where id = new queryParam({value: arguments.id, cfsqltype: 'CF_SQL_VARCHAR'})",
datasource: application.dsn
});

Having to use a Query object is also a bit frustrating although I think I can just tack execute().getResult() onto the statement above, but I'd have to check. It would be much easier to wrap the query object and have it call a query function, which in turn would function just like cfquery:

var myQuery = query(sql="SELECT col from table where id = new queryParam({value: arguments.id, cfsqltype: 'CF_SQL_VARCHAR'})", datasource: application.dsn);

Those are some initial thoughts for ColdFusion syntax changes. Please feel free to post your own in the comments below.

20 comments:

  1. The only ones I can really agree with you on are 3 & 5. Your number 1 is just nit picky.

    ReplyDelete
  2. Nic, this may be the only time I completely agree with you. And Tim, it's not nit picky. It's standard object notation. Things like that and zero-indexing are not the differences people look for when choosing a tech.

    ReplyDelete
  3. Your colleague Mike voiced the same idea about zero-indexed arrays in the Day 2 "Meet the CF Team" keynote. They didn't say they'd go that route, but they didn't oppose the idea.

    ReplyDelete
  4. I STRONGLY agree with 1, 2 and 4. Things like arrays, structures and other standard elements that learning the CF language frustrating. Even though backward compatibility is important, if there was a way to specify it in app.cfc(even if it forces CF to use a different compiler, as mentioned at RIAcon), it would be easy enough to upgrade legacy apps while making a CF take a step toward the future.

    While its nice to look backwards, its also nice update things to look forward a bit.

    ReplyDelete
  5. I don't think I'm following your rationale on what's wrong with the current Query object implementation.. what's wrong with this? (see psatebin below)

    http://pastebin.com/g5XVSFuW

    Seems a lot nicer and cleaner to me than:

    var myQuery = query(sql="SELECT col from table where id = new queryParam({value: arguments.id, cfsqltype: 'CF_SQL_VARCHAR'})", datasource: application.dsn);

    ReplyDelete
  6. There are two types of people in the world.

    1. Those who think arrays should be 1-indexed.
    1. Those who think arrays should be 0-indexed.

    ReplyDelete
  7. @Tim - 1 isn't nit-picky IMO, and of course all of this is just my opinion ;) Following ECMAScript syntax is a good thing, and allows an easy transition to and from other languages.

    @Mike - I don;t think it should be a different compiler. I think it should be fixed. I just think they should then give some path to backwards compatibility as it really would affect 3rd party stuff for some time. That's why I think it should be app scoped.

    @Phill - I'd like to first see params defined within the context of the Query() class, and moreover I'd rather see a query function than a query object. THis would keep it in line with other tags that have moved to script.

    ReplyDelete
  8. So you want ColdFusion to be like other languages (#2) but not like other languages (#6)?

    ReplyDelete
  9. I'll tell you Jason, I;m really struggling with #6. It just doesn't feel clean to me. It's one of those things I'll have to keep iterating over to figure out.

    ReplyDelete
  10. Heh, I just got the beauty of Scott's comment up there. How'd I miss that!

    ReplyDelete
  11. Man, #4 sure would be good. Yeah, it sure would. But - it's never going to happen. It's certainly NOT working now. Honest.

    ReplyDelete
  12. 1) Agreed.
    2) No. It's stupid. If you have to constantly explain why you start at 0, you failed.
    3) Would be cool.
    4) *No Comment*
    5) Meh. Would be nice I guess. But only as an alternative, not a replacement. I think for folks new to coding, arrayLen(arr) is simpler.
    6) God no. And actually, our syntax matches how it's done in other languages too, like SQLite in AIR.

    ReplyDelete
  13. So aside from MATLAB and Lau ( http://en.wikipedia.org/wiki/Comparison_of_programming_languages_(array)#Array_dimensions) everything uses start index of 0. Is it really a crime to have this.ZeroArrayIndex = true in Application.cfc?

    Still not sure on some of the other suggestions, but really like #1, #3, #4.

    I would like to see something that Railo did with the scope scanning being adjustable in CF 10.

    ReplyDelete
  14. 1) Agreed, tried to get it added during the days before CF9 was released and Adobe kept closing the tickets.

    2) Not sure how this can be done without having major backward compatibility issues.

    3) Again kept asking for it in the betas, and again Adobe kept closing the tickets

    4) Same as number 3

    5) Again it would be nice to have a language that was purely an OO object if you so desire, I am a big fan of being able to use objects and its internals rather than a new tag each time. It is going to get to a point in 5 years time that there will be something like 500 tags to know.

    6) No don't like that syntax at all, I think the current implementation is as good as your going to get.

    ReplyDelete
  15. @Ray re: #4, will that be documented and supported? Otherwise I can't use it. Also, pretty sure I tried it before, is that a 9.01 thing?

    ReplyDelete
  16. I figured I'd see what Railo does on all of these:

    1. { a: 1 } has been supported for quite a while (because, as people have pointed out, it made more sense - and I think { a = 1 } may have been a later addition to Railo?)

    2. 1-based for compatibility but a per-application config would be nice.

    3. works for arrays as well as structs. I agree that array= / index= ought to set the index to 1, 2, .. instead of the elements. Backward compatibility means the array= / index= / item= triple is probably the best way to go.

    FWIW, Railo used to treat for ( ix in myArray ) as setting ix to 1, 2, ... but changed it to for ( elem in myArray ) when ColdFusion added that syntax!

    4. Railo supports for ( colname in qry ) so you can, I believe, do this:

    for ( col in qry ) {
    for ( cell in qry[col] ) {
    ...
    }
    }

    If ColdFusion adds that as for ( row in qry ) then Railo will have to change to be compatible. I doubt many folks are relying on the column name iteration currently.

    5. len() works on all types in Railo so len(myStruct) = structCount(myStruct), len(myArray) = arrayLen(myArray), len(qry) = qry.recordCount and so on.

    6. Railo supports a pure script form of query, using writeOutput() to populate the SQL buffer:

    query name="myQuery" datasource="#application.dsn#" {
    writeOutput( "SELECT col from table where id = " );
    queryparam value="#arguments.id#" cfsqltype="varchar";
    }

    Arguably still ugly but much closer to the tag syntax.

    ReplyDelete
  17. Just noticed the blog ate my tags... #3 should say cfloop collection=... item=... works for arrays...

    ReplyDelete
  18. +1 on 1, 2, and 5. The others haven't affected me personally as much, but sound like legitimate concerns as well. Hope they address some of these.

    ReplyDelete
  19. @Sean - {a:1} is also how we do it in ActionScript (or any ECMAscript based language). That's interesting how Railo handles query, and you are right, not super pretty but much closer to tag based syntax.

    @Brian - #3 & #4 will getcha, just give it time ;)

    ReplyDelete