Recently, while drafting a paper I found myself wishing for arbitrary-arity macros in LaTeX.

Motivation

As concrete examples, consider two macros \List and \Tuple, that should render lists and tuples of arbitrary lengths, respectively. For example:

  • \List{1}{2} should generate $[ 1; 2 ]$,
  • \Tuple{1}{2}{3}{4} should generate $\langle 1, 2, 3, 4 \rangle$,
  • \List{1}{\ldots}{n} should generate $[ 1; \ldots; n ]$, and
  • \Tuple{x_1}{\ldots}{x_n} should generate $\langle x_1, \ldots, x_n \rangle$

These macros should just work; like any other LaTeX macro without defying common intuition, and without the cognitive burden of having to remember anything in particular about them.

Yes, I did try the obvious approaches ...

Of course, I could always write [1,2,3,4] and \langle 1,2 \rangle to generate the lists and tuples directly, but that takes away the key power of LaTeX and of macros — the ability to easily customize the formatting of all my lists. For example, if I wanted to generate comma-separated lists instead, like $[1,2,3,4]$, I would have to manually replace every semicolon with a comma, as opposed to simply tweaking my \List macro.

One idea, inspired by the cons construct from functional programming, is to define a simple \Cons macro:

\newcommand{\Cons}[2]{#1;#2}

and use it as [\Cons{1}{\Cons{2}{\Cons{3}{4}}}].1 Now I can simply tweak my \Cons macro to change the formatting of my lists globally. However, even besides the fact that I still can’t modify the brackets around the lists, the syntax is just too verbose. There had to be a better way to typeset this …

To be more specific, I was looking for a solution with these properties: (in that order of importance)

  1. No special syntax: The macro should look and behave just like regular LaTeX macros. For example, it shouldn’t parse a long string that uses another delimiter to separate the real arguments, like in case of \List{1|2|3|4}.2
  2. No special packages: Ideally one should be able to create and use these macros easily without having to install any third-party packages.
  3. No special context: The macros should not require special environments, nor should it make fragile assumptions such as no nesting etc. For example, \List {\List{1}{2}} {\List{1}} {\List{1}{2}{3}} should simply work as expected — render a list of lists: $[[1;2];[1];[1;2;3]]$.

Research

Some background and existing techniques

I was aware of variadic functions — a feature that is supported by most modern programming languages. So, I started to look for LaTeX’s equivalent of those. Well, to be precise, what I was actually looking for was a variadic macro. The C-family of languages support such macros via two special macros: __VA_ARGS__ and __VA_OPT__. However, after a fair amount of Googling and StackOverflowing, I was convinced that LaTeX didn’t have any built-in support for variadic macros.

But, I stumbled upon this question on TeX.StackExchange that discusses this exact problem, and some of the solutions were quite enlightening. The solutions were very close to what I was looking for, but there were two main aspects of these solutions that left me wishing for something better:

  • Violated P1: The commands had to be invoked via the \usedecl macro, so something like \usedecl{List}{1}{2}{3}.
  • Violated P2: They used third-party packages, such etoolbox or xparse.

After some more searching, I came across this blog post, which took the essence of these answers and outlined a very neat solution. With some minor modifications, I had my first working version of a variadic \List macro! 🎉

\newcommand{\GobbleListArg}[1]{;#1\CheckListArg}
\newcommand{\CheckListArg}{\csname @ifnextchar\endcsname\bgroup{\GobbleListArg}{]}}
\newcommand{\List}[1]{[#1\CheckListArg}
How does this work?

Here is how this macro works:

  1. The \List macro just outputs [ and the first argument #1, and then invokes the \CheckListArg macro.
  2. \CheckNextListArg checks the next character in the input stream using the \@ifnextchar kernel macro. If it finds \bgroup i.e. beginning of a LaTeX group, then it invokes \GobbleListArg, otherwise it outputs ] and halts.
  3. Finally, \GobbleListArg prints ; and the next argument (its first argument), and then recurses by calling \CheckListArg.

There are two key implementation details here:

  • The @ifnextchar macro must be wrapped within \csname and \endcsname because it contains the @ character. LaTeX macros names can only contain characters from the “letter” category, and @ is a character from the “other” category. One can also use \makeatletter and \makeatother, but I prefer this approach.
  • One cannot simply use an opening brace { after \@ifnextchar, although arguments to macros are grouped within opening and closing braces. It is a syntax error to not have a corresponding closing brace. An escaped brace \{ cannot be used either, because it’s not a category-1 (begin-group) token. The \bgroup macro expands to a category-1 (begin-group) token. Its counterpart \egroup expands to a category-2 (end-group) token.

You can read this TeX.StackExchange thread to learn more about category codes in LaTeX.

Great, so now I had a very simple and elegant approach for crafting variadic macros, and I soon found myself using a whole bunch of them, such as \List, \Tuple, etc. But for every new variadic macro, I had to create these two intermediate Check and Gobble macros too.

Since these macros have very similar structure, I began to wonder if I could write a macro that creates a variadic macro 🤯 along with its required intermediate macros. With such a generator macro — call it \VARIADIC — I could simply say \VARIADIC{List}... to create a new variadic macro named \List.

Solution

The variadic \List macro has three key components — a start symbol [, a mid symbol ;, and a stop symbol ]. My first attempt was to simply parameterize the macro name and these three symbols:

% USAGE :: \VARIADIC {name} {start_sym} {mid_sym} {stop_sym}
\newcommand{\VARIADIC}[4]{%
  \expandafter\newcommand\csname Gobble#1Arg\endcsname[1]{%
    #3##1\csname Check#1Arg\endcsname%
  }%
  \expandafter\newcommand\csname Check#1Arg\endcsname{%
    \csname @ifnextchar\endcsname\bgroup{\csname Gobble#1Arg\endcsname}{#4}%
  }%
  \expandafter\newcommand\csname #1\endcsname[1]{%
    #2##1\csname Check#1Arg\endcsname%
  }%
}

Now I could easily generate several variadic macros:

\VARIADIC {List} {[} {;} {]}                   % generates the same \List as above
\VARIADIC {Tuple} {\langle} {,} {\rangle}      % generates a variadic \Tuple macro
What is this sorcery though?

In addition to the main ideas behind the \List-specific macros above, there are two other key ideas here:

  • The \expandafter macro has been used to delay the expansion of \newcommand till its argument, in this case the name of the new macro, has been expanded. This is necessary since the macro names are constructed dynamically, e.g. Check#1Arg expands to CheckListArg for \VARIADIC{List}....
  • The # character has been escaped for arguments to the generated macros, e.g. while #1 expands to the first argument to \VARIADIC, ###1 expands to #1 which becomes the first argument to the generated macro.

Also see this TeX.StakExchange thread to learn more about \expandafter.

Awesome! But, there was still something missing … The \VARIADIC macros were not easily composable with other macros. For example, I wanted my \List and \Tuple macros to automatically use math mode by wrapping them inside \ensuremath. The following obvious hack does not work:

\VARIADIC {Tuple} {\ensuremath\bgroup\langle} {,} {\rangle\egroup}
Obvious approaches, failure, help from TeX gurus ...

Of course I could hardcode \ensuremath this within \VARIADIC itself, but I didn’t want it to be applied to every variadic macro. Plus, I wanted a more general solution that would allow me to apply an arbitrary macro (or environment). The main issue was delaying the application of the macro till the entire variadic part had been expanded. After a fair bit of hacking, I gave up and turned to the TeX gurus — I opened this question on TeX.StackExchange.

@siracusa came up with a brilliant solution — accept the macro name as a parameter, accumulate the variadic expansion result in an additional parameter to each intermediate macro, and then finally apply the provided macro in front of the fully expanded result.

To take this a step further and be able to apply environment around the variadic macros, I modified this solution and added another parameter that is a macro name which is applied right after the fully expanded result. This is useful for “closing” operations, such as \end-ing an environment.

✨ The final variadic-macro generator that applies two arbitrary macros before and after the variadic expansion is: ✨

% View a sample document: https://www.overleaf.com/read/bwkjcwcktsqd
%
% USAGE :: \VARIADIC {name} {before} {start} {mid} {stop} {after}
\newcommand{\VARIADIC}[6]{%
  \expandafter\newcommand\csname Gobble#1Arg\endcsname[2]{%
    \csname Check#1Arg\endcsname{##1#4##2}%
  }%
  \expandafter\newcommand\csname Check#1Arg\endcsname[1]{%
    \csname @ifnextchar\endcsname\bgroup{\csname Gobble#1Arg\endcsname{##1}}{#2{##1#5}#6}%
  }%
  \expandafter\newcommand\csname #1\endcsname[1]{%
    \csname Check#1Arg\endcsname{#3##1}%
  }%
}

Now I could define things like:

\VARIADIC {List} {} {[} {;} {]} {}                             % a vanilla variadic macro
\VARIADIC {Tuple} {\ensuremath} {\langle} {,} {\rangle} {}     % wrapped inside \ensuremath
\VARIADIC {HugeList} {\begin{Huge}} {[} {;} {]} {\end{Huge}}   % wrapped inside Huge environment

% One can even apply an environment and a macro
\VARIADIC {TinyTuple} {\begin{tiny}\ensuremath} {\langle} {,} {\rangle} {\end{tiny}}

Bonus Solution

At this point, it’s straightforward to create a fold version of the \VARIADIC macro that takes an arbitrary binary macro, and “reduces” the given arguments by repeating applying this macro.

% View a sample document: https://www.overleaf.com/read/bwkjcwcktsqd
%
% USAGE :: \FOLDVARIADIC {name} {before} {binary_macro} {after}
\newcommand{\FOLDVARIADIC}[4]{%
  \expandafter\newcommand\csname FoldGobble#1Arg\endcsname[2]{%
    \csname FoldCheck#1Arg\endcsname{#3{##1}{##2}}%
  }%
  \expandafter\newcommand\csname FoldCheck#1Arg\endcsname[1]{%
    \csname @ifnextchar\endcsname\bgroup{\csname FoldGobble#1Arg\endcsname{##1}}{#2{##1}#4}%
  }%
  \expandafter\newcommand\csname Fold#1\endcsname[2]{%
    \csname FoldCheck#1Arg\endcsname{#3{##1}{##2}}%
  }%
}

FAQs

  1. Any gotchas? …
    The only ones I know of are standard LaTeX gotchas. LaTeX ignores spaces or newlines between argument groups, e.g. \textcolor{red}{red text} is equivalent to \textcolor {red} {red text}. This might lead to \VARIADIC macros consuming more arguments than you intended, e.g. \List{1}{2} {\bf X} would render $[1;2;{\bf X}]$.
  2. Can I use multiple macros in the before argument?
    Not directly, but you could create a intermediate unary macro that applies your macros to the its input, and use this intermediate macro as the before argument.
  3. Does it work with MathJax?
    Unfortunately no, at least I wasn’t able to get it working. I found that MathJax doesn’t understand the \expandafter macro. If you know a way to make this work on MathJax, please let me know.

Go ahead and try some examples on Overleaf. Cheers! 🙂

  1. This isn’t exactly the type-correct cons operator, but let’s not be pedantic and make things harder than necessary. 

  2. Disclaimer: I am prioritizing convenience over robustness here. Although the approaches I discuss in this post are fairly robust, they involve low-level handling of braces. If you notice something that doesn’t work with \VARIADIC macros, please let me know!