Thursday 20 February 2014

ColdFusion 11: good stuff

G'day (again!):
But it's not all bad. Some of the things I've looked at so far have worked well.

Member functions

Once I got TestBox working (see "ColdFusion 11: first bug. Bad bug."), I was able to run those unit tests I wrote yesterday ("TestBox, BDD-style tests, Railo member functions and bugs therein"). I'm pleased to say that ColdFusion 11's member function implementations for struct functions are pretty good. Here's the results (just the issues, not the successes):
  • +new() tests (9 ms)


    • has not been implemented (8 ms) - The incoming function threw exception [Object] [The new method was not found.] [Either there are no methods with the specified method name and argument types or the new method is overloaded with argument types that ColdFusion cannot decipher reliably. ColdFusion found 0 methods that match the provided arguments. If this is a Java object and you verified that the method exists, use the javacast function to reduce ambiguity.] different than expected params type=[expression], regex=[.*]
That's OK. That test was in there for completeness only. It makes no sense to call a .new() method on an existing struct. Maybe as a static method on the Struct class, but we don't have those, so fair enough.
.find() here works exactly the same as structFind(), which is a bug... but an existing bug (3710341). However it should be fixed, and now is the time to do it.

Adobe have not implemented the .get() method. This is no great loss, but for the sake of completeness should be there (3710345).

They've also not implemented the .filter() method, which is a bit of an oversight! Raised as 3710336.

And structKeyTranslate() is a Railo-only function, and one of dubious merit, so it's right the tests fail there.

Now it looks like I'm just raising more bugs here (and, well, I am), but bear in mind that the tests for all the other methods worked perfectly, and cross-compatible with Railo. Adobe have done a good job here.

They've done member functions for the following data types:
This is better coverage than Railo has. Good work. I've not tested 'em all yet, but I will do.

First-class functions

Built-in CFML functions are now "first class" and can be treated as values. Here's a silly example:

//firstClass.cfm
stringModifier = function(s, f){
    return f(s);
};

s = "G'day World";
writeDump([
    stringModifier(s, ucase),
    stringModifier(s, reverse),
    stringModifier(s, len)
]);

This outputs:

array
1G'DAY WORLD
2dlroW yad'G
311

Not the most handy thing ever, but as an architectural thing, it makes CFML seem that little bit more "grown-up".

Null-coalescing operator



Update 2020-09-09

For the sake of full disclosure, my position has now changed on this. My initial position (as per below) was based on my poor understanding of other language's implementations of the "elvis operator".

IMO the ?: operators should expect a boolean value as the first operand, not potentially a null. Null in CFML is not considered a falsey value, and is invalid for use in a situation requiring a boolean. The short-circuit ?: operator should work exactly the same as the long-hand ternary operator, other than one can omit the "if true" value, where the result of the expression is simply the first operand if it's true or truthy.

If CFML was to be implementing a null-coalescing operator - something different from the short-circuit ternary - it should perhaps have better used ??.

However the horse has bolted, and I will concede I was an initial voice in encouraging this particular misstep on Adobe's part.

This is playing catch-up with Railo a bit. The null-coalescing operator is a binary operator that works thus:

result = firstValue ?: secondValue

The rule being that the result will be the firstValue if it is not null, otherwise it will be the second value. Simple. Both Railo and now ColdFusion have messed it up a bit though, confusing the concepts of "null" with "not defined".

This demonstrates the correct operation of the null-coalescing operator:

//nullCoalescingOperatorCorrect.cfm
nullVariable = javaCast("null", "");
variableToSet = nullVariable ?: "default value";

writeDump({variableToSet=variableToSet});

This outputs:

struct
VARIABLETOSETdefault value

because nullVariable is null, it's not used for the value of variableToSet; "default value" is instead.

However this demonstrates where CFML goes a bit wrong:

// nullCoalescing.cfm
variableToSet = notDefined.invalidProperty ?: "default value";

writeDump({variableToSet=variableToSet});

This should error. Because notDefined isn't null, it's not defined. And something that isn't defined cannot have a property (. operator), so notDefined.invalidProperty is just an error situation. However this "works" in CFML:

struct
VARIABLETOSETdefault value

I raised this yesterday with Railo: "Probable bug in null-coalescing operator", and Igal and I just got the hump with each other, but Gert is erring towards "right or wrong, I'd prefer 'wrong'". I'd prefer "right". The way to solve it so that everyone is happy would be to additionally implement the safe navigation operator, as I mention in an earlier article: "Thinking about operators in CFML". This way we have the ?. operator acting how it's supposed to, and the ?: acting how it's supposed to. Not implementing ?: so that it alters how the . operator works.

I'm gonna raise a bug (3710381) & and E/R (3614459) here.

But having the null-coalescing operator is a good 'un.


I'm gonna press "send" on this now... I've got some other stuff to look at and my eyeballs are gonna fall out of their sockets if I stare at this computer for too much longer (I've been at it for seven hours non-stop so far today, and it's still only 2pm).

--
Adam