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
\csname
and\endcsname
must not be enclosed in curly braces (for a reason unknown to me, let me know if you know why). - Putting a space before
\endcsname
will 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. ↩︎