Wednesday, 21 August 2013

CFML: What's the truth?

This might be painfully obvious to everyone, but I had a "what the hell?" moment this morning when having a look at some of Aurel's code posted against one of my earlier articles. It turns out I have the wrong expectations as to what and when CFML considers values to be boolean.

I've gotta be quick as it's taken most of my lunch break to distill the code down to something simple, but here's the crux of the matter:

truths = [-2,-1,1,2,true,'"yes"'];

for (A in truths){
    test(A, true);
    for (B in truths){
        same = A==B;
        bothNumeric = isNumeric(A) && isNumeric(B);
        expression =
        test("#A# EQ #B#", same || !bothNumeric);

function test(expression, expected){
    var result = evaluate("#expression# ? true : false");
    var colour = result == expected ? "green" : "red";

    writeOutput('<span style="color:#colour#">#expression#: #result#</span><br>');

Do you like my - fairly legitimate, I think? - use of evaluate() in there? ;-) But that's an aside. Here's the output:

-2: true
-2 EQ -2: true
-2 EQ -1: false
-2 EQ 1: false
-2 EQ 2: false
-2 EQ true: false
-2 EQ "yes": false

-1: true
-1 EQ -2: false
-1 EQ -1: true
-1 EQ 1: false
-1 EQ 2: false
-1 EQ true: false
-1 EQ "yes": false

1: true
1 EQ -2: false
1 EQ -1: false
1 EQ 1: true
1 EQ 2: false
1 EQ true: true
1 EQ "yes": true

2: true
2 EQ -2: false
2 EQ -1: false
2 EQ 1: false
2 EQ 2: true
2 EQ true: false
2 EQ "yes": false

true: true
true EQ -2: false
true EQ -1: false
true EQ 1: true
true EQ 2: false
true EQ true: true
true EQ "yes": true

"yes": true
"yes" EQ -2: false
"yes" EQ -1: false
"yes" EQ 1: true
"yes" EQ 2: false
"yes" EQ true: true
"yes" EQ "yes": true

This is courtesy of, btw.

To explain, all the values in the truths array are boolean TRUE values. This is demonstrated in the first line of each test round. That being the case, testing whether two TRUE values EQual each other should result in a TRUE. I will except the situations in which both values are numeric, because then a numeric comparison should be made, and they'll only be TRUE if they're both the same.  However in the situations in which both operands are not numeric, then they should both be treated as booleans, in which case all these expressions are basically "TRUE EQ TRUE". Which last time I checked is TRUE. This seems partially true (!) if one operand is numeric and specifically 1 (as opposed to "non-zero", which was my understanding of what's TRUE in CFML: "1 EQ TRUE" is TRUE. This does not make sense to me.

However I suspect I am missing something. If just CF was reflecting this, then I'd say "bug". But Railo works the same way, and they're less likely to screw this sort of thing up, and also given both platforms behave the same suggests to me it's expected.

So... err... WTF?


PS, for a bonus bit of confusion... yesNoFormat() treats an empty string as a boolean falseno.