Wednesday, 20 November 2013

Ways to call functions in CFML

We've got Brad Wood and David Epler to thank for this article (sorry that Twitter presents information back to front, so start at the bottom and read up. Like one never generally would):

Ignore Brad's fairly facile common-sense-defying focus on semantical apologetics, and more on the nub of the issue: CFML is rather lacking in uniformity when it comes to how to call functions (which - to Brad's credit - he does agree to be an issue).

Native CFML has these options:

Ordered arguments:

result = functionName(arg1, arg2[... , argn])

Named arguments:

result = functionName(arg1=val1, arg2=val2[..., argn=valn])

Via an object instance:

object = new Component();


result = object.method();

And for bespoke (ie: the code you and I write) we have all three options too, plus an additional one:

Via an argument collection:

args = {arg1=val1, arg2=val2[..., argn=valn]}
result = functionName(argumentCollection=args)


The problem is that all bespoke code supports all three options (well: provided the code is not simply a stand-alone UDF, and the third option doesn't therefore apply). However any given native CFML construct only supports a subset of those options.

I can use ordered arguments on any built-in CFML function, eg:

item = listGetAt(list, index);

However I can only use named arguments on a small subset of functions:


One cannot do this:

item = listGetAt(list=list, position=index);

Or perhaps a better example wherein one isn't just specifying the arguments in order anyhow:

matches = reFind(reg_expression="\b\w{4}\b", string=text, returnsubexpressions=true); // third arg - start - omitted as the default is fine

And also there's the aberration that some CFML constructs need to be called as object methods, rather than functions (eg: running a query with Query.cfc... thankfully fixed in ColdFusion 11, and already in Railo).

Dave contends this is a bug in CFML... the language design has become flawed in that it allows a mishmash of these three different approaches, in often a mutually-exclusive sort of way.

I agree with Brad that reFind() (for example) is not documented as being able to be called using named arguments or an argument collection (good: because it can't) , but that's - I suspect willfully, but I don't know why - missing the point. The bug is that as soon as CFML started to allow alternate syntaxes for passing arguments to functions, in that it did not implement it across the board. That's a bug. The work is incomplete. Or the work as prescribed by the design was completed (ie: the design said: "do it for bespoke code but not native constructs. Except these ones"), and the design was incorrect.

Brad positions this as being an enhancement request because "bug" only means "doesn't do what the docs say". However one can equally say it's not an enhancement to suggest the language should work as anyone with any wits about them would expect it to. It's "getting something up to reasonable minimum expectations" is not an enhancement. It's fulfilling the requirements. Be they explicitly stated, or can be reasonably inferred.

Put in simple terms, poor design of the language has caused it to be buggy because it's lost uniformity of syntax. It's a regression, of sorts.

Still: whatever we call it, it should be dealt with. All functions - whether inbuilt or bespoke - should be invokable via all available syntax constructs. This would make code look more uniform, and be a win for CFML.

So go vote for the bu^h^henhan^h^h^h^h^hticket. ;-) It's "Accept an assignment statement as a parameter for built-in CF functions" (3540467).