Common Lisp's version of printf
is the famously overpowered format
. In addition to everything printf
does, it has conditionals, iteration, recursion, case-folding, tabulation and justification, pretty-printing, English plurals and number names (cardinal and ordinal), and two kinds of Roman numerals. Surprisingly, in a language where almost everything is user-accessible, most of these features are not available separately, only through format
. The complexity is such that there's a compiler to make format strings execute faster. Sadly (or not, depending on your perspective) it's not Turing-complete, but that's only because it has no way to store information.
Of course there's a way around that. None of format
's features is quite as overpowered as ~/
, which calls arbitrary functions: (format stream "~/package:function/" x)
does (package:function stream x nil nil)
. (The two nil
s are for some other features not used in this case. Yes, there are more. Are you surprised? And yes, in practice the explicit package name is required.) User extensibility is a good principle, but this seems silly. Why would you ever want to call a function through format
instead of doing so directly?
Well, the other day I had to generate some XML in C, and the obvious way was with printf
:
fprintf(somefile, "<element attr=\"%s\" />", somestring);
But there might be XML-meaningful characters in the string, and I didn't want to mess around with fancy XML-generation libraries. So I had to either escape the string before printing...
char buffer[BIG_ENOUGH]; /* yeah, right */
escape_xml(buffer, BIG_ENOUGH, somestring);
fprintf(somefile, "<element attr=\"%s\" />", buffer);
...or give up on printf
, which was what I ended up doing:
fprintf(somefile, "<element attr=\"");
print_xml_string(somefile, somestring);
fprintf(somefile, "\" />");
Either option destroys the clarity of the printf
. What I really wanted was a custom printf
operation, like this:
fprintf(somefile, "<element attr=\"%/escape_xml/\" />", somestring);
That's exactly what format
's ~/
command does:
(format somefile "<element attr=\"~/xml:escape/\" />" somestring)
I guess it's not so silly after all. It is verbose (imagine repeating ~/xml:escape/
for each of a dozen attributes), but that's fixable. If there were an interface for defining new format
commands, then any frequently used ~/
function could be given a one-character form, and all would be well, except possibly for readability. (Although in this case all of the obvious characters x e & < s
are already taken.) Lisp being Lisp, you generally can get at the implementation's way of defining format commands, e.g. sb!format::def-format-directive
, but depending on implementation internals is not usually a good idea. Exposing this interface would make format
more malleable, like the rest of the language, and would also make its long feature list easier to swallow.
For new languages, though, I think I prefer string interpolation, which avoids the issue entirely:
(put somefile "<element attr=\"$(xml:escape somestring)\" />")
It would also be nice to have a choice of string delimiters, so the quotemarks don't require escaping. But that's a different, less interesting issue.