Fangyi Zhou
Meta-Programming in LaTeX
Many researchers write paper in $\LaTeX$ because it provides a convenient macro system that makes typesetting maths equations easy (or difficult, when it doesn’t work).
One way to write papers in $\LaTeX$ is to use as few “advanced features” as possible, for a vanilla experience: no fancy packages, just use the minimal setup. Gradually, as the paper starts to grow long, you begin to think of adding some macros for things that are too long to type, for example:
\newcommand{\lCalc}{$\lambda$-calculus}
\newcommand{\pCalc}{$\pi$-calculus}
\newcommand{\myTool}{\textsc{MyTool}}
Using macros can help improve consistency in writing, and hopefully reduce a few keystrokes. However, some macros may be very similar, like the following:
\newcommand{\redx}{{\color{red} x}}
\newcommand{\redy}{{\color{red} y}}
\newcommand{\redz}{{\color{red} z}}
When you realise red is maybe no longer your favourite colour, and decide to change everything into blue. You may think about doing some refactor like this to make future changes easier:
\newcommand{\termcolour}{\color{red}}
\newcommand{\termx}{{\termcolour x}}
\newcommand{\termy}{{\termcolour y}}
\newcommand{\termz}{{\termcolour z}}
Then you may want to do the same for other syntactic categories, e.g. types, type variables, … and okay, maybe some automation is needed.
Meta-Programming?
The term meta-programming can be interpreted in many ways. In this context, we want to write a command (i.e. a macro) to generate other comamnds (i.e. macros). It is analogous to template programming in C++ or macro programming in C. Fortunately, $\LaTeX$ is perfectly capable of allowing users to make macros to create other macros, because EVERYTHING IS A MACRO 1.
So intuitively, we want to have something like:
\makeMacro{term}{x}
to expand into
\newcommand{\termx}{{\termcolour x}}
We are encounted with an immediate question: how do we create make a macro with a non-constant name?
Using \csname and \endcsname to create a control sequence
After some time googling around and consulting this TeX FAQ, I learnt these two $\TeX$ primitives for marking the beginning and the end of a control sequence (in other words, macro names). See also this nice article.
Therefore, we can do something like:
%%% !!! THIS DOES NOT WORK !!!
\newcommand{\makeMacro}[2]{%
% #1 is the prefix, #2 is the variable
\newcommand\csname #1#2\endcsname{%
{\csname #1colour\endcsname #2}%
}%
}
We manage to construct a control sequence for our output macro: #1#2 will be
expanded into \termx when passing {term} and {x} as arguments.
Moreover, the colouring macro will be generated by #1colour, which will be
expanded into \termcolour.
A few important notes here:
- The
\csnameand\endcsnamemust not be enclosed in curly braces (for a reason unknown to me, let me know if you know why). - Putting a space before
\endcsnamewill actually consider space as part of the control sequence, so no extra space before\endcsname.
However, the snippet above won’t work — it will produce an informative
error message:
! LaTeX Error: Command \csname already defined.
Or name \end... illegal, see p.192 of the manual.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.13 \makeMacro{term}{x}
It seems that we are trying to define \csname, which is not what we want.
A closer look at TeXFAQ reveals the missing ingredient: \expandafter.
Also, not sure where I can find a manual and check p.192
So we need to \expandafter
Intuitively, \expandafter delays the expansion of a macro.
In our use case, we want the control sequence between \csname and
\endcsname to be expanded first, then the expanded result can be used as an
argument to \newcommand.
However, \expandafter is known to cause possible confusions, as shown in
here,
and here, as well as
multiple questions on TeX StackExchange.
For the sake of my own sanity, I decided not to divulge too much into details
and take it at face value.
So this is the final result:
\newcommand{\makeMacro}[2]{%
% #1 is the prefix, #2 is the variable
\expandafter\newcommand\csname #1#2\endcsname{%
{\csname #1colour\endcsname #2}%
}%
}
Still, a Minor Caveat
However, there is minor caveat that commands invoked with \csname and
\endcsname will default to \relax (i.e. noop) if that command is not
defined.
This may be tricky to debug, as things may fail silently leaving you puzzled
why your colour does not appear.
One way to address this issue is to test whether the command #1colour exists
before invoking it, with the eTeX primitive \ifcsname.
So, for the extra mile:
\newcommand{\makeMacro}[2]{%
% #1 is the prefix, #2 is the variable
\expandafter\newcommand\csname #1#2\endcsname{%
\ifcsname #1colour\endcsname
{\csname #1colour\endcsname #2}%
\else
Missing colour macro for {#1}
\fi
}%
}
That’s it!
With some meta-programming, I managed to create the package
magicvariables for an
extended version of the example covered in this article, and the package
atendofenv for adding a small
custom symbol at the end of an environment.
I hope this article is helpful for creating you own macros!
Maybe a bit exaggerating. ↩︎