Monday, 5 May 2014

Looping in CFML: community input solicited

Yesterday I wrote-up my experiences with the new cfloop() construct in ColdFusion 11: "ColdFusion 11: cfloop in CFScript very broken". The same construct exists in Railo, btw (it's nowhere near as broken).

I raised a ticket with Adobe summarising my findings: 3754577. Overnight Rupesh has come back to me, with this comment:

Though we have added a generic approach for the script syntax for all tags, it does not work in cfloop's case as the tag implementation is directly in the generated code. the exception being query or file and that is why looping over a query or file works but others don't.
Though it would be ideal to do this for completeness, is this really required? We already have cfloop equivalent in cfscript - using "for", "for-in" and "while" loops. file is the only thing that is not supported by "for" or "for-in". I feel we should stick to the "for", "for-in" & "while" and throw a good error for cfloop with indexed, array, list, collection and condition. Thoughts?

It's great to get decent, thoughtful, feedback. I put my own thoughts down in response:

I'm split on this on.

On one hand if you're gonna do this porting of tags to CFScript with this generic approach rather than with a situation-specific one, then I think the approach should be all-encompassing. If it's generic don't second-guess stuff, just do it.

On the other hand, I think perhaps having implemented cfloop() at all was perhaps a bad move. Instead it would have been better to augment the existing CFScript looping construct: for(). There's no reason why that could not have accommodated query- and file-looping functionality. The benefit of this is that for() is a familiar construct for everyone, and it doesn't necessitate yet another looping construct.

What you've ended up with is - same as with the list member functions - a situation where the language isn't "hackable". One cannot simply infer how the looping works from the behaviour of existing constructs. There is no rhyme or reason why one would use for() for arrays, but cfloop() for queries. It makes no sense. One needs to know the inner workings of what the CF Team decided to know how things work, which I think is fairly opaque language design.
I am going to solicit more community input on this, to see what people think.
I decided not to add it to my comment on the bug tracker, but I'll add it here. I also think too often the Adobe team opt for "what's quickest/easiest" rather than "what's best". And I really think this needs to stop. The language needs to come first, then how much effort is involved comes secondary to that. It would be better to not do it at all, rather than to an incomplete job due to timesaving.

A case in point is this looping thing. In CFScript we now have:

  • for() - indexed, array, collection
  • while() / do() - conditional
  • cfloop() - files, queries
  • listEach() - lists
  • type-specific iteration functions
(I separate those last two out as there's no "procedural" way of looping over a list in CFScript, I think; one needs to use the functional way).

Adding in yet another way of doing looping just to cover files and queries seems wrong to me. They should have either just added in the appropriate iteration functions, or added functionality to for() to handle it. Or both. Both would have been the better solution. But cfloop() is just PHPifying to me.

But, anyway, it's not all down to just what I think (and I wouldn't even want that to be the case). I was hoping you people could pitch in on the conversation too. Care to add your comments onto that ticket I linked to above?