ECL Feature of the Week – #EXPORTXML
A recent forum question asked if it were possible to concatenate all of the fields of a file without having to type them all in. In that particular case the fields were fixed length and the same type so our compiler writer was able to advise the use of a type transfer – simple, slick and efficient.
Of course life isn’t always that clean; if some of the fields had been a different type – or if some form of separator had been required between the fields then a type transfer would not come even close to working.
Fortunately ECL has a rather more sophisticated (though rather more complex) way to deal with all of the fields of a record: compile time introspection.
Introspection has been around in some languages for years although it has really been popularized by Java. Put simply introspection allows the shape of a record or class, the number of fields, their types and possibly some other information to be examined by the code. This makes it possible to write code that alters its behavior based upon the types it has been given; sort of “infinite polymorphism, for free”.
Sounds too good to be true? It is. The problem is that most languages that provide introspection provide it at run-time. In order to provide run time introspection each run-time object has to carry around some baggage; and the ‘infinitely polymorphic’ routine has to figure out what it is doing – each and every time. Thus the ‘for free’ is true regarding how much code your write – but not how long it takes to execute.
ECL seeks to solve this problem by providing compile time introspection. At run time everything is locked and rigid (and therefore very fast); but at compile time you can write routines that adapt to the shape of the file they have been handed. Here is a macro to perform a concatenate of all fields – regardless of type:
MAC_ConcatAll(InputFile) := MACRO
#EXPORTXML(AllFields, recordof(inputFile))
#FOR (AllFields)
#FOR (Field)
(STRING)InputFile.%@name% +
#END
#END
''
ENDMACRO;
The ‘magic’ happens with the #EXPORTXML statement; this moves the structure of ‘inputfile’ (represented as XML) into a template symbol called AllFields – then the ECL Template language can be used to walk over the XML, generating code. In this case the only meta-data I actually use is the name of the field; but lots of other goodies are available such as type and size.
The MACRO can be called like a regular MACRO provided you have an XML scope active; if you don’t then you need a dummy LOADXML to create one. Here is a very simple calling example:
LOADXML('');
r := RECORD
STRING f1;
STRING f2;
UNSIGNED f3;
END;
d := DATASET([{'The answer to life the universe and everything',' : ',42}],r);
r1 := RECORD
STRING all_of_them;
END;
r1 tra(d le) := TRANSFORM
SELF.all_of_them := Mac_ConcatALL(le);
END;
PROJECT(d,tra(LEFT))
As with many of ECLs more powerful features it is possible to write some genuinely horrible code using the ECL Template Language. All the usual rules apply – abstraction is good provided the whole problem can be genuinely abstracted. If you find yourself hacking heuristics into your templates then it is time to stop and code for each record separately. But for those short and simple tasks that are easy to define cleanly – compile time introspection is a neat way to re-use code without losing efficiency.