Wednesday 18 July 2012

The IMP Operator

G'day
Everyone knows about boolean operators such as AND, OR and NOT.  I was surprised one day - about a year ago - when I tried to create a variable"imp" (or "iMP" or whatever... I forget what the situation was now) that I got an error, replicated here:

The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request

Invalid CFML construct found on line 1 at column 8.

ColdFusion was looking at the following text:imp
The CFML compiler was processing:

  • A cfset tag beginning on line 1, column 2.
The error occurred in D:\websites\www.scribble.local\junk\junk.cfm: line 1
1 : <cfset imp = "foo">

That didn't give me much to go on, but eventually I just started changing random things (not an approach to problem-solving I recommend, btw ;-), including the variable name, and the problem went away.  I changed the variable name back to "imp" and the error came back.  At which point I consulted Google, and it pointed me to the docs for  Operator types: Boolean operators.



This lists the usual suspects, as well as XOR, which I'd've assumed ColdFusion had but have never had cause to use, another one called "EQV" which I'd not heard of but could guess what it's for, and then this "IMP" operator. Huh?

I have to say that I was a bit gobsmacked that I had been using ColdFusion for over ten years at this point, and I still wasn't aware of three of its boolean operators!

Anyway, IMP is quite a cool operator!  Its official explanation is thus (copied from the docs I pointed to above):

IMP
Implication: The statement A IMP B is the equivalent of the logical statement “If A Then B.” A IMP B is False only if A is True and B is False. It is True in all other cases.

Fair enough.  Here's that in a truth table:

XYX IMP Y
TRUETRUETRUE
TRUEFALSEFALSE
FALSETRUETRUE
FALSEFALSETRUE

If we were to express this without using IMP, we'd have this (pseudo-code):

if X is true then
    if Y is true then
        result = true
    else
        result = false
    /if
else
    result = true
/if


Contrast that with:

result = X IMP Y

Yeah, one could get it all into one expression without using IMP with the tenary operator:

result = X?Y==true:true

But I think a lot of people will look blankly at that.  Hey, I guess a lot of people will look blankly at the "X IMP Y" too, but at least they're just a google away from an explanation.

Where's the IMP operator handy?

Consider validation of an optional field.  There's an optional email field, but if the user fills it out, you want to make sure the value is a valid email address (email address validation is a topic for another day, I think.  I manage to have a strong opinion on it (*)).

One would do this:

<cfif len(form.emailAddress) IMP isValid("email", form.emailAddress)>
    <!--- all good --->
<cfelse>
    <!--- kick 'em back to the form with a suitable message --->
</cfif>

That's quite tidy, innit?

However... my choice of sample code conveniently avoids what I think is a bug in CF's IMP operator.  Well if not a bug, then an implementation shortcoming.

Like the OR and AND operators, the IMP operator could safely use short-circuited evaluation: if the first operand is false, there's no need to worry about the second operator because the result will definitely be true (the latter two rows of the truth table).  Unfortunately IMP does not use short-circuited evaluation. This is a shame, because it means it's unusable for what's perhaps the best use case of it.

In the example above, I'm using a form field, and given it's clearly gonna be sourced from a text input on the form, the form variable will "always" exist, so I don't need to check for existence: a length-check is adequate.

However what if the situation was slightly different, and the rule was "if the variable exists at all, then it must hold an email address", eg:

<cfif structKeyExists(arguments, "emailAddress") IMP isValid("email", arguments.emailAddress)>
    <!--- all good --->
<cfelse>
    <!--- kick 'em back to the form with a suitable message --->
</cfif>

This will error if arguments.emailAddress doesn't exist, unfortuately.  If IMP used short-circuited evaluation it would work fine.

Just to clarify, a parallel (but not equivalent) with AND would be this:

<cfif structKeyExists(arguments, "emailAddress") AND len(arguments.emailAddress)>
    <!--- all good --->
<cfelse>
    <!--- kick 'em back to the form with a suitable message --->
</cfif>

And this works fine, because AND is short-circuited, so as soon as one expression is false, it stops checking subsequent expressions. I might cover short-circuited evaluation in another posting, in case it's not clear what I'm on about.

So it's a bit of a let-down that IMP is hamstrung like that.  I've mentioned it to Adobe, but it so far has very little traction.

I'd be interested to know - if indeed anyone is actually reading all this bumpf -whether or not you've encountered the IMP operator before, and whether or not you use it.

--
Adam

(*) I have strong opinions on most things, granted.  This is simply no exception :-)