Saturday 9 January 2016

Reflection on CFML tags and functions

G'day:
And I don't mean that in the philosophical or introspective sense. I mean actual reflection: being able to programmatically address the CFML language itself.

This line of thinking has come from the ColdFusion 2016 pre-release programme, and as such I cannot link to or discuss what's being said there. I do think there are some public links to bits and pieces of the topic somewhere, but I'm buggered if I can find them at the moment. Anyway: everything I say here is my own "work", and is original to this article, so I feel fine opening it for public discussion.

CFML has a function getFunctionList()which returns a liststruct with a key for each function name in CFML. I'm not sure who thought that was a clever implementation, but they should perhaps not be able to make any unsupervised decisions in future.

If returns this sort of thing:


Mind blowing.

Oddly, there's also a way of accessing information about those awful "service" CFCs ColdFusion has shipped with for a while (you know: Query.cfc, Http.cfc etc, in the customtags/com/adobe/coldfusion directory). For that, one looks directly in the server scope:


And as they're just CFCs, one can also get - slightly more useful - info about them with getMetadata():


For CFML tags, one needs to look in cfusion/wwwroot/WEB-INF/cftags/META-INF/taglib.cftld (which despite its name is XML):




And for member functions, one pretty much needs to use Java Reflection:

public class coldfusion.sql.QueryTable
 extends coldfusion.sql.Table
 extends coldfusion.sql.imq.imqTable
 extends java.lang.Object
 implements javax.sql.RowSet, coldfusion.wddx.RecordSet
{

 // [...]

 /*** METHODS ***/
 
 // [...]

 public int addColumn(java.lang.String, java.lang.String, java.util.Vector)

 // [...]

}

But also be prepared to wade through the rest of the morass of public methods which Adobe have - for some reason - exposed to the world.

Seriously Adobe: what the f*** was going through your head when you decided on any of that? (yes, I know some of it possibly predates Adobe).

Now the conversation is that whilst there getFunctionStructList(), there's no getTagStructList(). And there should be. My contention is oh no there bloody shouldn't.

Another contention has been perhaps instead of a function, Adobe could perpetuate that garbage they've slapped in the server scope. That's an even worse suggestion, IMO.

Both suggestions seem to be mired in the way we might have been writing code or "designed" the language in the early 2000s. I have no doubt a lot of people still write their CFML this way (hey, I know a lot of people still write their CFML code that way), but can we move on and be slightly more sophisticated and thoughtful these days?

A good precedent has been set by Java (and, yeah, other languages... even PHP for goodness sake!) here in the usage of reflection: basically being able to access and modify metadata about objects at runtime. Because - let's face it - CFML constructs are just objects (in the notional sense, not the practical implementation sense).

In Java one can take an object - say a string - get its class, and then get data out of the class such as its methods, its constructors, its inheritance tree, and a whole swag of stuff. That ClassViewer thing I have spouted about in the past ("ClassViewer.java") uses reflection to render metadata about Java classes. I use it all the time.

One sticking point is that when using reflection, one calls the reflection methods on a class, which often one derives from an object instance of that class (but can do it on the class itself). So if we were going to implement an analogous... um... implementation (I hate it when I don't plan my sentences properly. If only I could go back and reword them after I type them...) for CFML, and the intent was to get back the metadata about tags and headless functions, then they'd need to actually be exposed by a class. This is not a show-stopper though, perhaps there should be a CFML class or something? Then we could have methods thus:

String[] CFML.getTagNames() // an array of strings each of which are the name of a tag:




TagMetadata CFML.getTagByName(String tagName) // takes eg, "cfabort" and returns an object representing that cfabort's metadata


TagMetadata[] CFML.getTags() // returns all the Tag objects for all tags


And do the same with functions, eg:

FunctionMetadata CFML.getFunctionByName(String functionName) // takes eg, "arrayappend" and returns an object representing arrayAppend()'s metadata


FunctionMetadata CFML.getFunction(Function function) // eg: CFML.getFunction(arrayAppend), remembering that built-in functions are first class these days.


Then once we have TagMetadata objects, one can call methods like getAttributeSignature() (remembering that tags have varying combinations of which attribute combinations are valid, and which ones are optional, etc). Same with functions.

Equally, one should be able to use some mechanism to get member functions from CFML's own data types, without all the crufty publicly-exposed Java shite too. Perhaps:

ClassMetadata = CFML.getClass(NativeType object) // eg: CFML.getClass(someStruct) // Class metadata for structs, upon which one can call getMethods() or something.


We should also be able to use this process on CFCs and objects thereof too, I think?

This treats CFML and our own code in a more sophisticated, functional (ie: it's useful, not as in "functional programming"), and uniform fashion. And it's not complicated.

This is the sort of thing we should be aiming for when wanting metadata from CFML constructs. Not a liststruct of string names, or something in the server scope, or tucked away in some file.

Thoughts? I'm sure this is too complex an enhancement request to suggest for CF2016, but perhaps something to look at for (CF2016)+1.

Right. Back to Fallout 4 for the rest of the day.

--
Adam