Skip to main content

Lua for LaTeX commands

·306 words·2 mins

TeX remains the best system for typesetting documents. However, as a language it feels antiquated and verbose. The mathematical content of the document can get lost as a result. Lua to the rescue.

I am currently reading Types and Programming Languages by Benjamin C. Pierce. Answers to the exercises I record in my personal knowledge management system managed. In some embed some term from (a variant of) the lambda calculus inside a mathematical environment—eventually to be rendered by LaTeX. However even writing simple mathematical terms such as

20220205162025-blog_ce19d4b36a4ca0325d34e6f79de7b790f36dd88b.svg

becomes tedious:

\text{test} = \lambda{}l.\;\lambda{}m.\;\lambda{}n.\; l\; m\; n

LaTeX‘ math typesetting engine gobbles up any whitespace between symbols and so I add \; to separate binders and terms and for function application. Ideally, I would like to write this in a more natural style:

test = λl. λm. λn. l m n

Writing a procedure to translate the latter into the former in LaTeX would be time consuming. Fortunately, I use Lua(La)TeX for its support for unicode and modern fonts. LuaLaTeX, as its name suggests, joins the worlds of LaTeX and Lua. Through its embedded Lua interpreter, it is possible to extend LaTeX using a more familiar programming style. So, I wrote the following function in Lua instead and saved it to lambda.lua:

function lambda_expression(s)
  s = s:gsub("[%s]+", "\\;")
  s = s:gsub("([a-zA-Z][a-zA-Z]+)", "\\text{%1}")
  s = s:gsub("λ", "\\lambda{}")
  s = s:gsub("%(", "\\left(")
  s = s:gsub("%)", "\\right)")
  return tex.sprint(s)
end

I then put the following code in my LaTeX‘ preamble. The luacode environment makes the function available to the Lua-interpreter. The ulam command can then be used to invoke the function from LaTeX. It does so by encapsulating its argument in a string:

\usepackage{luacode}
\begin{luacode}
dofile("lambda.lua")
\end{luacode}
\newcommand{\ulam}[1]{\luaexec{lambda_expression("#1")}}

With that done, I can write

\[ \ulam{test = λl. λm. λn. l m n} \]

and get a beautifully typeset lambda expression.