Friday 21 February 2014

Can we please agree that Adobe is not the arbitor of what constitutes an integer?

G'day:
My... what a lot of ColdFusion chatter this is at the moment. It's bloody good I'm on holiday at the moment so I can keep up with it (and... erm... instigate some of it... ;-).

But for this article / gripe session, I want to reiterate an old article I wrote about integers. And ire.

Mary Jo put this back on my radar, with a comment in one of my articles from y/day:

Yes, seriously. In other words, some developers might write code relying on the wrong behavior so we can't fix it because then their code wouldn't work anymore. Once again catering to the ones that don't know what they are doing and making things far more difficult for the rest of us. Perfect case in point is the broken behavior of isValid('integer') which to my knowledge is still broken.
This is in reference to ticket 3169196, which is about ColdFusion not knowing what is or is not an integer. I reference this in an article "Survey Results: CF strings being interpreted as dates and integers", as well as in this one: "According to ColdFusion "0,6" == "6,0". And both are integers to boot".

Mary Jo's reminding me of this just got me annoyed again. She also asked to verify it's still a problem in ColdFusion 11, and I can confirm that it is:

values = ["100", "100.00", "1,000", "1,000.00", "$1,000", "$1000.00", "$,1,2,$,2352345,$", "0,6", "1,2,3,4"];

values.each(function(value){
    var expression = 'isValid("integer", "#value#")';
    writeOutput("#expression#: #evaluate(expression)#<br>");
    try {
        writeOutput("Can be <em>used</em> as integer by CFML: ");
        1 MOD value;
        writeOutput(" YES");
    }catch(any e){
        writeOutput(" NO: #e.message#");
    }
    writeOutput("<br><hr>");
});

Here we loop over an array of values, some of which are definitely integers, some of which could conceivably considered integers, and some that Are. Not. Bloody. Integers. And I loop over that array, test whether ColdFusion thinks each is an integer, and then actually uses the value as an integer (well... MOD actually accepts any numeric, despite the fact it should only accept an integer, but that's another story, and my point still stands). So let's see how this code runs... on ColdFusion 11...

isValid("integer", "100"): YES
Can be used as integer by CFML: YES


isValid("integer", "100.00"): NO
Can be used as integer by CFML: YES


isValid("integer", "1,000"): YES
Can be used as integer by CFML: YES


isValid("integer", "1,000.00"): NO
Can be used as integer by CFML: NO: The value 1,000.00 cannot be converted to a number.



isValid("integer", "$1,000"): YES
Can be used as integer by CFML: NO: The value $1,000 cannot be converted to a number.


isValid("integer", "$1000.00"): NO
Can be used as integer by CFML: NO: The value $1000.00 cannot be converted to a number.


isValid("integer", "$,1,2,$,2352345,$"): YES
Can be used as integer by CFML: NO: The value $,1,2,$,2352345,$ cannot be converted to a number.


isValid("integer", "0,6"): YES
Can be used as integer by CFML: YES


isValid("integer", "1,2,3,4"): YES
Can be used as integer by CFML: NO: The value 1,2,3,4 cannot be converted to a number.



I've marked a few things here:
  • where ColdFusion gets it right (unambiguously);
  • where ColdFusion arguably gets it right (I'm allowing thousand separators and decimals where they are all zero);
  • where ColdFusion gets it wrong;
  • Where ColdFusion isn't even internally consistent.
Faced with this, Adobe's position is:

  • Rupesh Kumar
    2:53:06 AM GMT+00:00 Apr 24, 2012
    This has always been the behavior and changing this would result in backward compatibility issue. It will not be fixed.

Now. Adobe, pay attention. The concept of "integer" is not something that Adobe made up. You don't get to decide what is an integer. An integer is defined thus:

Integer; from Wikipedia:
An integer is a number that can be written without a fractional or decimal component.

Specifically in the context of computer science; from Wikipedia:

An integer value is typically specified in the source code of a program as a sequence of digits optionally prefixed with + or -. Some programming languages allow other notations, such as hexadecimal (base 16) or octal (base 8). Some programming languages also permit digit group separators.
Nowhere does it say "oh, you know... it can be any old shit really... just something numbery; or, f*** it, something numbery with commas and currency symbols. [shrug]"

And also nowhere does it say it's up to Adobe to decide what's a frickin' integer.

The isValid("integer") functionality is broken, Rupesh (sorry mate, but you're the one who posted the message on the bugbase saying it was "OK"). It's a very fundamental piece of functionality, and it's broken and not even internally consistent with other areas of ColdFusion (which are also questionable in how they handle what ought to be "integer" values).

There is no backwards compatibility issue here. If any developer has leveraged the existing faulty behaviour to illegitimate ends... they're mistaken in their actions. The bug they are leveraging should be fixed, and their code should break as a consequence. Tough shit. The ColdFusion language should not be held to ransom by people who can't use the thing properly, nor should it be held to ransom by Adobe ColdFusion Team members who lack common sense and professional judgement as to when to apply the "backwards compatibility" mantra.

An integer is an integer. I think we could let it slide to include thousand separators and digits beyond a decimal point as long as they are zero. But that's it. Anything else is not an integer. This is not open to interpretation. Fix the function. Stop hamstringing our language via shockingly poor decisions like the ones you have made along these lines.

For the sake of completeness, this is how Railo handles that same code:

isValid("integer", "100"): true
Can be used as integer by CFML: YES


isValid("integer", "100.00"): true
Can be used as integer by CFML: YES


isValid("integer", "1,000"): false
Can be used as integer by CFML: NO: can't cast [1,000] string to a number value


isValid("integer", "1,000.00"): false
Can be used as integer by CFML: NO: can't cast [1,000.00] string to a number value

isValid("integer", "$1,000"): false
Can be used as integer by CFML: NO: can't cast [$1,000] string to a number value

isValid("integer", "$1000.00"): false
Can be used as integer by CFML: NO: can't cast [$1000.00] string to a number value

isValid("integer", "$,1,2,$,2352345,$"): false
Can be used as integer by CFML: NO: can't cast [$,1,2,$,2352345,$] string to a number value

isValid("integer", "0,6"): false
Can be used as integer by CFML: NO: can't cast [0,6] string to a number value

isValid("integer", "1,2,3,4"): false
Can be used as integer by CFML: NO: can't cast [1,2,3,4] string to a number value


That is all internally consistent, and also completely acceptable. It doesn't allow for thousand separators (fine), and does allow for zeros on the right hand side of the decimal point. Fine.

To be honest, I think the way Adobe operate in regards to some decisions like this, the decisions should be taken out of their hands. CFML used to have an advisory committee... I think perhaps it should be resurrected. Adobe are too married to their mantra of "backwards compat at all costs, even at the cost of common sense", and I think this is detrimental to CFML. It'd be great if we could get together a community committee who is interested in CFML, not just Adobe's bottom line or mantras, because I think they're too introspective sometimes. But that's a discussion for another day.


For now I shall go look at some new feature of ColdFusion 11 and report on that.

Cheers.

--
Adam