Printf supremacy
Not all functions are equal.
Some simple, others not, some are safe, not all, some have side effects, and some don't.
There is, however, a class of functions that are above all others,
a higher cast, respected by compilers and language designers.
Think of the most priviledged functions in Pascal,
those that are blessed with the ability to receive variable number of arguments.
Or in case of C, of these only functions to get their arguments dependently-typed-checked.
Read along as I will try to convince you, the printf supremacy is real.
Printf functions are the ones used to output formatted text on the terminal.
I claim that they are priviledged in many languages and their special treatment is out of place.
It started with Fortran. There was no single function to do formatted output in Fortran,
there was a special statement to do that. Here is how it looked like:
WRITE OUTPUT TAPE 6, 601, IA, IB, IC, AREA
FORMAT (4H A= ,I5,5H B= ,I5,5H C= ,I5,
& 8H AREA= ,F10.2, 13H SQUARE UNITS)
Needless to say, it was a distinguished language element,
both the programmer and the compiler must have had a respect for it.
And the statement itself looked quite involved,
for a good reason -- check out that 601 after the WRITE keyword, it stands for the line number of the relevant FORMAT form!
Going forward in history...
-
Algol implemented printf as a function, but unlike the usual printf, the format string that it received was not a regular string,
but rather a separate entity whose value must have been available at compile-time,
and which had its own, special constructor-syntax.
This restriction has been later adopted by other languages in ML-family, notably OCaml.
-
C's printf does not look outstanding from the language point of view, but it is handled with care in today's compilers.
Pass a wrong type of argument that does not comform to the format string,
or God forbid, use a dynamically generated format string, and you will get a warning from GCC.
No other regular function can possibly express such behaviour,
one would need dependent types for that,
but there is a compiler-specific extension that allows one to bless any function with such a check
by simply adding the following magic to its signature:
__attribute__(format(printf, ...))
It may also be interesting to note that there are not that many variable-arity functions in the C standard library, and their semantics is usually a complication for compiler writers, considering the relative simplicity of C.
- Pascal's write was very exceptional in that it was one of a few privileged functions
that were allowed to accept variable number of arguments.
- Even though OCaml dropped special syntax of Algol's printf,
the format string still had to be a coward and not a normal string.
- Python 2, a dynamic language with great potential in teaching,
was shameless enough to interpret print as a statement.
-
Rust's printf! pretends to be a macro, but searching for the implementation we encounter this piece of code:
macro_rules! format_args_nl {
($fmt:expr) => {{ }};
($fmt:expr, $($args:tt)*) => {{ }};
}
There are actually not that many excuses to not have printf! as a macro in rust,
the behaviour could be replicated, but for some reason it was decided to have it as a builtin.
Printf is so complicated it has been considered an attack surface.
This seems to be the primary reason for it being so extraordinary, for it is hard to express it in native terms.
But when we do make it special-case, we lie to programmers.
I personally think that printf is a hack, it's ugly, and does not belong in modern languages,
but it is here anyways, and we should aknowledge that it is a little special.
References
- printf's history
- First language to have printf
- Mechanics of Algol's printf
- Confused user tries to understand C's printf
- Critique of printf (rust case)
- Attack on printf
return home