Monday 31 October 2022

CFML: AND and OR operators not doing what one might expect

G'day:

A question came up on the CFML Slack forums today about some "unexpected behaviour" with ColdFusion's and operator. Here's an example (CF2021):

cf-cli>5 && 7
7

cf-cli>5 || 7
5

Compared to Lucee:

CFSCRIPT-REPL: 5  && 7
true
CFSCRIPT-REPL: 5 || 7
true

The questioner was puzzled by ColdFusion's behaviour, expecting to see something more like Lucee's result.

The docs for these (Expressions-Developing guide › Boolean operators) says this:

AND or &&
Return True if both arguments are True; return False otherwise. For example, True AND True is True, but True AND False is False.

On the face of it Lucee is getting it right, and ColdFusion is being weird, however this is a case of Lucee following the docs, and the docs being wrong.

CFML's behaviour with the AND and OR operators is not quite as the docs say, they behave the way described clearly in the JavaScript docs (JavaScript › Expressions and operators › Logical AND (&&)):

More generally, the operator returns the value of the first falsy operand encountered when evaluating from left to right, or the value of the last operand if they are all truthy.

I only cite the JS docs cos they're clear and well-worded, not because of any connection to CFML.

I guess it works like this in languages with truthy/falsy values as opposed to strict true/false because the operand-values can have meaning beyond just being boolean.

I checked some other languages I had to hand:

adam@DESKTOP-QV1A45U:~$ node -i
> 5 && 7
7
> 5 || 7
5

(And JS run in the browser is the same)

Ruby:

adam@DESKTOP-QV1A45U:~$ irb
irb(main):001:0> 5 && 7
=> 7
irb(main):002:0> 5 || 7
=> 5
irb(main):003:0>

Groovy:

groovy:000> 5 && 7
===> true
groovy:000> 5 || 7
===> true
groovy:000>

PHP:

php > echo 5 && 7;
1
php > echo 5 || 7;
1

Clojure:

user=> (and 5 7)
7
user=> (or 5 7)
5

There's a strong precedent there, but obviously it could have gone either way. CFML just went the way shown. So it's a bug in Lucee that it doesn't work the same as ColdFusion.

BTW: ColdFusion has worked that way since at least CF5. I just checked.

Why am I writing this? Well cos it's not correctly documented anywhere, so I figured I'd jot something down in the hope Google picks it up.

Righto.

--
Adam