Tuesday, 29 April 2014

CFML: I know you are, you said you are, but what am I?

This is for Brad and Christopher and Mark.

st = {};
o = new C();

cfcInstance = createObject("java", "coldfusion.runtime.TemplateProxy");
structInstance = createObject("java", "coldfusion.runtime.Struct");
MapInterface = createObject("java", "java.util.Map");

<h3>Is it a struct?</h3>
An actual struct: #structInstance.getClass().isInstance(st)#<br>
A CFC instance: #structInstance.getClass().isInstance(o)#<br>
<h3>Is it a CFC instance?</h3>
An actual CFC instance: #cfcInstance.getClass().isInstance(o)#<br>
A struct: #cfcInstance.getClass().isInstance(st)#<br>
<h3>Is it a Map?</h3>
An CFC instance: #MapInterface.getClass().isInstance(o)#<br>
A struct: #MapInterface.getClass().isInstance(st)#<br>


Is it a struct?

An actual struct: YES
A CFC instance: NO

Is it a CFC instance?

An actual CFC instance: YES
A struct: NO

Is it a Map?

An CFC instance: YES
A struct: YES

Here's the method signature for structKeyExists() (I decompiled source):

static boolean StructKeyExists(Map struct, String key)

Why do both work fine with struct functions? Because struct functions don't take structs are their arguments. They take Maps. And both ColdFusion structs and ColdFusion CFC instances extend (different types of ~) Maps:


public final class coldfusion.runtime.Struct
 extends coldfusion.util.FastHashtable
 extends coldfusion.util.CaseInsensitiveMap
 extends java.lang.Object
 implements coldfusion.runtime.CloneableMap, coldfusion.monitor.memory.MemoryTrackable, coldfusion.runtime.CFComparable

CFC instance:

public class coldfusion.runtime.TemplateProxy
 extends coldfusion.runtime.LocalScope
 extends coldfusion.runtime.Scope
 extends java.util.AbstractMap
 extends java.lang.Object
 implements coldfusion.runtime.Invokable, coldfusion.runtime.NamedInvokable, coldfusion.runtime.Metadata, java.io.Serializable, coldfusion.runtime.CFComparable

In short: CFC instance make no claim to being structs. But they don't need to, because struct functions don't require the first argument to be a struct, they just need to be some type of Map. Just because two types of object share some behaviour does not imply one is a sub-type of another. All they do is share a common ancestor. Like bonobos and humans. But no-one is suggesting bonobos are types of human, or vice versa (although sometimes I do wonder, with some people).

Objects are not structs.

If there was a ColdFusion function isMap(), I would be completely content with both returning true for that. However an object is not a struct, so should not return true from isStruct().

As I alluded to: the fact that it does return true is representative of the same ill-conceived train of thought that has ColdFusion claiming "6,0" is a valid integer, etc ("Of integers and indolence", and others).



PS: from Urban Dictionary.