Wednesday 22 January 2014

Where Railo is smart and ColdFusion is dumb

G'day:
This line of investigation stemmed from this ticket in the bug tracker: SerializeJSON turns strings that look like scientific notation into floats. The contention from Adobe as to why this can't be fixed is:

At the time of serialization , datatype information of an object is not available (cause CF is typeless) and hence knowing the original datatype and convert it according is not possible sometimes and this is one of those cases.

That this is "not possible" can only be taken in the context of ColdFusion, because Railo deals with it just fine:

st = {
    i = 1138,
    f = 11.38,
    s = "1138"
};    
json = serializeJson(st);
writeDump(var=[st,json]);


ColdFusion:

array
1
struct
F11.38
I1138
S1138
2{"F":11.38,"S":1138,"I":1138}

They're all numerics. Even the string.

Railo:

Array
1
Struct
F
number11.38
I
number1138
S
string1138
2
string{"S":"1138","I":1138,"F":11.38}

Railo knows a string when it sees one.

This perhaps draws attention to the difference between the notion of "typeless" and "loosely-typed". I generally shy away from the idea of "typeless" because you can't tell me that a string and and array can be used interchangeably in CFML. They can't. There are type considerations. Variables very much have a type. They're not typeless. However CFML will do its best to convert between types as required:

i = 1138;
s = i & i;
f = i / 3;
d = dateFormat(i);
writeDump(var=[{i=i},{s=s},{f=f},{d=d}]);
 
Array
1
Struct
I
number1138
2
Struct
S
string11381138
3
Struct
F
number379.333333333333
4
Struct
D
string11-Feb-03

I've used what is an integer value as a string, float, and date there. No prob. Loosely-typed.

But let's have a look at some other code, under the hood:

<cfset i = 1138>
<cfset f = 11.38>
<cfset s = "1138">

ColdFusion compiles this down to:

// [...]
protected final void bindPageVariables(VariableScope varscope, LocalScope locscope){
    super.bindPageVariables(varscope, locscope);
    this.F = bindPageVariable("F", varscope, locscope);
    this.S = bindPageVariable("S", varscope, locscope);
    this.I = bindPageVariable("I", varscope, locscope);
}
// [...]
protected final Object runPage(){
    // [...]
    this.I.set("1138");
    this.F.set("11.38");
    this.S.set("1138");
    // [...]
}
// [...]

And Railo does this:

// [...]
paramPageContext.us().set(KeyConstants._I, Caster.toDouble(1138.0D));
paramPageContext.us().set(KeyConstants._F, Caster.toDouble(11.380000000000001D));
paramPageContext.us().set(KeyConstants._S, "1138");
// [...]

Railo has the presence of mind to preserve some of the typefulness, because - I guess - they have the presence of mind to realise that that's information that shouldn't be discarded, it just shouldn't be the last word as to the variable's type. Loosely-typed vs typeless.

I think ColdFusion could get away with its approach back when the only thing CFML code would be talking to is other CFML code. But not that it's de rigeur for a loosely-type CFML system to be communicating with a typeful external system (Java, JSON, etc), then the ColdFusion approach is demonstrating itself to be less than ideal.

And - back to the JSON issue at hand - ColdFusion needs to be able to reliably serialize CFML data to JSON. This isn't really a "nice to have" these days. So I think they need to have a bit more of a think as to how to achieve this than to decide "sorry, not possible". I realise that the ticket is still marked "To Fix" / "Investigate", and that Awdhesh was not suggesting that was the final word on the subject.

Redoing how variables are initially saved to preserve their type would be too much of an undertaking at this juncture in the CF11 dev cycle (it's about to go beta), so what do you think? How to approach this?

--
Adam