Treating \(\mathrm{\LaTeX}\) like a programming language: the case of color-coded equations

code
latex
Author

Michael DeCrescenzo

Published

August 31, 2022

\(\newcommand{latex}{\mathrm{\LaTeX}}\)

\(\latex\) can be a headache. The syntax is clunky. What seems like “normal” usage requires a lot of frustrating patterns. I see academics and other researchers complain about it, and I usually agree with the spirit of the complaints.

But I don’t always agree. I often feel like many researchers don’t get the most out of \(\latex\) (hereafter “LaTeX” or “TeX”). When I used to write more LaTeX in graduate school, I knew I didn’t. I mean, I was superficially fine at it. I was capable of injecting statistical results into documents, managing citations and cross-references, and so on. But TeX requires a lot of shoeleather work—boilerplate code to itemize and enumerate, manage environments, typeset math—and I didn’t know how to criticize that very well. Like many others, I would repeating my code a lot, get trapped in irritating patterns, and feel the age of the language constantly.

But you get older, you (hopefully) get better at programming, and you realize what you were being silly about. LaTeX is a programming language. You can do ordinary programming language things with it, like save variables and write functions. And then you can turn those variables and functions into interfaces that let you work with nicer abstractions with greater efficiency. And these interfaces can make LaTeX more joyful.

This has been on my mind for a little while, but recently I was talking with Jordan Nafa about color-coding different parts of some equations. So that will be our example for this post: color-coding an equation. We require no deep LaTeX expertise, external packages, or complicated programming concepts. Just a little ordinary programming thinking to turn LaTeX’s built-in color tools into something more practical and friendly.

Color fundamentals in LaTeX

Like many things in LaTeX, the built-in experience of color-control is a little clunky. You can change text color with \color{}, which naturally takes some color argument. The language provides builtin keywords like red and blue, which are a bit harsh on the eyes, but others like violet, teal, and maroon are fine. But the color options aren’t the problem. The problem is the interface, which works like this.

Any time you change a color, you change it indefinitely (up to some scope change, more on that in a second). Here is an example:

We start with some normal text.
\color{red} And now the text is red indefinitely.

When I render this in the browser:

\[ \text{ We start with some normal text. \color{red} And now the text is red indefinitely. } \]

You may have encountered similar behavior with altering text size.

We start with some normal text.
\huge And now the text is huge indefinitely.

\[ \text{ We start with some normal text. \huge And now the text is huge indefinitely. } \]

We can extert some control over the “indefinite” application of these settings by introducing some scope, for instance with curly braces.

We start with some normal text. 
{ \color{red} But the } redness is { \color{red} contained inside curly braces}.

\[ \text{ We start with some normal text. { \color{red} But the } redness is { \color{red} contained inside curly braces}. } \]

This isn’t the worst but it doesn’t feel too comfortable. It would feel better to control colors more like this:

\red{But the} redness is \red{contained}

which makes the whole experience feel more intuitive, function-oriented and declarative. We will work toward something that feels similar (not identical) to this.

Doing better, with an equation example

We will work with an example equation that has certain terms that we want make different colors. More specifically, we want to map certain “semantic features” of the equation to certain colors in a consistent way. And we would like the interface to be minimally burdensome; I don’t want to have to type (or think) too much to get nice effects.

Let’s meet our equation and its “semantics”. By “semantics” I mean that the terms in the equation have some additional meaning in addition to the math itself. In this example the semantics refer to the levels at which terms are indexed. We collect data on some individuals subscripted \(i\), who are located within groups \(g\), and measured across time units subscripted \(t\). We model some outcome \(y_{it}\) as varying across individuals within groups and over time, \[\begin{align} y_{it} &= \alpha + \mu_{i} + \gamma_{g[i]} + \tau_{t} + \varepsilon_{it} \end{align}\] where \(\alpha\) is a constant term, \(\mu_{i}\) is a term that is fixed for an individual \(i\) across repeated observations, \(\gamma_{g[i]}\) is a group-specific that is fixed across time for all \(i\) in group \(g\), \(\tau_{t}\) is a time-unit effect that is fixed for all individuals. and \(\varepsilon_{it}\) is random error. These units of measurement—units, groups, time periods—are the semantics that we want to map to colors.

We have already identified one problem: we don’t want colors to apply indefinitely. This means that in order to turn “off” a color, I either have to explicitly call \color{black} again, or I have to scope the color e.g. with curly braces.

% back to black
y_{it} &= \alpha + \color{violet} \mu_{i} \color{black} 
          + \gamma_{g[i]} + \tau_{t} + \varepsilon_{it} \\

% use scope
y_{it} &= \alpha + {\color{violet} \mu_{i}} 
          + \gamma_{g[i]} + \tau_{t} + \varepsilon_{it}

\[\begin{align} y_{it} &= \alpha + \color{violet} \mu_{i} \color{black} + \gamma_{g[i]} + \tau_{t} + \varepsilon_{it} \\ y_{it} &= \alpha + {\color{violet} \mu_{i}} + \gamma_{g[i]} + \tau_{t} + \varepsilon_{it} \end{align}\]

Using the curly braces is definitely safer than \color{black}; I don’t want to assume that we always want to return to black. But managing the curly braces yourself can be cumbersome if you aren’t used to writing LaTeX that way already. I don’t write with that style, so I don’t want to burden myself with unusual patterns.1

So to improve things, we will introduce a function that, at first, will not feel like much of an improvement. But we discuss it to highlight both how we can modify interfaces with pretty simple tools and why we may want to do that. So, consider a function called setcolor, which takes two arguments: a color code and the text you want apply the color to locally.

% notice the extra {} braces in the definition
\newcommand{\setcolor}[2]{ {\color{#1} {#2}} } 

% apply function to the equation
y_{it} &= \alpha + \setcolor{violet}{\mu_{i}} 
          + \tau_{t} + \varepsilon_{it}

\[\begin{align} \newcommand{\setcolor}[2]{ {\color{#1} {#2}} } y_{it} &= \alpha + \setcolor{violet}{\mu_{i}} + \gamma_{g[i]} + \tau_{t} + \varepsilon_{it} \end{align}\]

Why do I say that this function may not feel like much of an improvement? It has some drawbacks: it really isn’t any “faster” to type \setcolor{violet}{\mu_{i}} than it is to type {\color{violet} \mu_{i}}. It actually has more characters and just as many curly braces. But it is better in at least two important respects that we should care about when we write code. One, we made the problem of locally scoping the color inherent to the function instead of being procedurally managed ad hoc outside of the function. This is good because it makes the whole thing more bug-proof. It also makes the interface feel more naturally function-like: we achieve a coherent result by calling a function with a predictable interface, then our work is done. No managing other special characters in the language as a side concern. It is easier to remember one thing (use a function) than it is to remember two things (use a function oh and also manage the weird scope). So we get a safer function with a more recognizable interface. Not so bad!

But we aren’t done there. We complete the interface by using this function to map semantics to colors directly. We create a function called \unitfx{} which applies the same color to any term in the equation that semantically refers to unit-level effects. Same for a functions called groupfx{}, \timefx{}, and so on. We also throw in a generic \param{} function for other terms.

\newcommand{unitfx}[1]{\setcolor{violet}{#1}}
\newcommand{groupfx}[1]{\setcolor{green}{#1}}
\newcommand{timefx}[1]{\setcolor{orange}{#1}}
\newcommand{param}[1]{\setcolor{maroon}{#1}}

So as long as we define these color commands in one place, all the hard work is done. All downstream calls to these functions are simple. Just wrap a term in the equation inside of the function corresponding to its semantic.

y_{it} &= \param{\alpha} + \unitfx{\mu_{i}}
          + \groupfx{\gamma_{g[i]}} + \timefx{\tau_{t}}
          + \param{\varepsilon_{it}}

\[\begin{align} \newcommand{unitfx}[1]{\setcolor{violet}{#1}} \newcommand{groupfx}[1]{\setcolor{green}{#1}} \newcommand{timefx}[1]{\setcolor{orange}{#1}} \newcommand{param}[1]{\setcolor{maroon}{#1}} y_{it} &= \param{\alpha} + \unitfx{\mu_{i}} + \groupfx{\gamma_{g[i]}} + \timefx{\tau_{t}} + \param{\varepsilon_{it}} \end{align}\]

Conclusion

So that’s it. Our solution meaningfully improves the color experience in LaTeX using just five lines of code: one to create a helper function and four more to create some color mappings. The helper function let us change the interface to color control in the general case. And the color mappings let us apply the new interface to create simple key-value pairs that map a semantic to a color. And bonus: the interface is also safer because managing the scope of a color modification requires no more work for the user.

We also do this without introducing any dependencies. We could still load outside packages to access more color values, but that choice doesn’t bear on the general interface or vice-versa. That modularity is a good design feature.

And all because we thought about a LaTeX problem like a programming problem.

Footnotes

  1. Although I could totally see, for example, Lisp users feeling quite comfortable with that style. It feels more like the (function arg1 arg2) syntax.↩︎