100 Languages Speedrun: Episode 28: TeX

100 Languages Speedrun: Episode 28: TeX

TeX (pronounced like "tech") is a typesetting system, which is still used for writing research papers in Mathematics, Computer Science, Physics, and related disciplines, and pretty much nothing else. Everyone else moved on to either WYSIWYG editors like Word, or to HTML, or maybe Markdown or something like that. But it still survives in its niche.

Like every typesetting system, it comes with its own embedded programming language, and that's what we're going to use.

Technically TeX is just the original system, and we'll also be using a lot of *TeX things (MacTeX, LaTeX, pdftex etc.), but that's meaningless distinction, so I won't mention it anymore.

Hello, World!

\documentclass{minimal}

\newcommand\hello[1]{Hello, #1!}

\begin{document}
\hello{World}
\end{document}

Which with pdflatex hello.tex generates a PDF page with the text "Hello, World!" (only relevant part shown):

hello.png

What's going on here:

  • \documentclass{minimal} defines the kind of document we're making - as we're not really interested in any typesetting, we can just use minimal. The most common type for actual documents would be article.
  • then follows basically equivalent of HTML <head> part
  • \begin{document} ... \end{document} is like HTML <body> ... </body>
  • We define \hello command with \newcommand\hello[1]{Hello, #1!} - the [1] says it takes one argument, then in the function body #1 refers to that argument.
  • We call the function with \hello{World} - there are no parentheses here, we defined explicitly it takes exactly one argument.

Loop

Let's try to setup a loop from 11 to 20. Obviously the system comes with lists already (numbered with \begin{enumerate} \item A \item B \end{enumerate}, bulleted with \begin{itemize} \item A \item B \end{itemize}, and all the other usual kinds), but we'll be doing a FizzBuzz shortly.

\documentclass{minimal}
\setlength{\parindent}{0pt}
\usepackage{ifthen}

% \numberloop{A}{B} prints all numbers from A to B
\newcommand\numberloop[2]{
  \newcounter{i}
  \setcounter{i}{#1}
  \whiledo{\not{\value{i}>#2}}
    {
      \thei
      \ifthenelse{\equal{#2}{\thei}}{.}{,}
      \stepcounter{i}
    }
}

\begin{document}
\numberloop{10}{100}
\end{document}

Which generates this:

loop.png

What's going on:

  • TeX has annoying default of having paragraph indent, we need to get rid of it with \setlength{\parindent}{0pt}
  • we import a package called ifthen with \usepackage{ifthen} - it contains some control structures \ifthenelse and \whiledo
  • % indicates line comments
  • we define command \numberloop that takes two arguments
  • inside it we define integer variable ("counter") i and set it to #1 (first passed argument)
  • there's nothing like ... <= ... in a loop, so loop condition must be \not{... > ...} (or +1 one of the sides)
  • inside the loop body, \thei means "current value of i counter"
  • \ifthenelse{\equal{#2}{\thei}}{.}{,} prints appropriate separator - , normally, but . in the final iteration.
  • \stepcounter{i} increases counter i by 1 each time

FizzBuzz

\documentclass{minimal}
\setlength{\parindent}{0pt}
\usepackage{ifthen}
\usepackage{intcalc}
\usepackage{multicol}

% \fizzbuzz{N} prints Fizz, Buzz, FizzBuzz, or N, according to the usual rules
\newcommand\fizzbuzz[1]{
  \ifthenelse{\equal{\intcalcMod{#1}{15}}{0}}{FizzBuzz}{
    \ifthenelse{\equal{\intcalcMod{#1}{5}}{0}}{Buzz}{
      \ifthenelse{\equal{\intcalcMod{#1}{3}}{0}}{Fizz}{#1}
    }
  }
}

% \fizzbuzzloop{A}{B} prints all FizzBuzz entries from A to B
\newcommand\fizzbuzzloop[2]{
  \newcounter{i}
  \setcounter{i}{#1}
  \whiledo{\not{\value{i}>#2}}
    {
      \fizzbuzz{\thei}
      \linebreak
      \stepcounter{i}
    }
}

\begin{document}
\begin{multicols}{4}
\fizzbuzzloop{1}{100}
\end{multicols}
\end{document}

Which generates this:

fizzbuzz.png

A few things are going on here:

  • we import package for integer math \usepackage{intcalc}
  • we import package for multi-column layout \usepackage{multicol}
  • \fizzbuzz{N} does the FizzBuzz for one number
  • \fizzbuzzloop{A}{B} does the FizzBuzz for all numbers from A to B, with line breaks in between
  • we wrap it all in a 4-column layout so the screenshot looks better

Fibonacci

\documentclass{minimal}
\setlength{\parindent}{0pt}
\usepackage{ifthen}
\usepackage{intcalc}

% \fib{N} returns the Nth Fibonacci number
\newcounter{j}
\newcounter{fiba}
\newcounter{fibb}
\newcounter{fibc}
\newcommand\fib[1]{
  \setcounter{j}{1}
  \setcounter{fiba}{1}
  \setcounter{fibb}{1}
  \whiledo{\value{j} < #1}
    {
      \setcounter{fibc}{\intcalcAdd{\thefiba}{\thefibb}}
      \setcounter{fiba}{\thefibb}
      \setcounter{fibb}{\thefibc}
      \stepcounter{j}
    }
  \thefiba
}

% \fibloop{A}{B} prints all Fibonacci numbers from A to B
\newcommand\fibloop[2]{
  \newcounter{i}
  \setcounter{i}{#1}
  \whiledo{\not{\value{i}>#2}}
    {
      \fib{\thei}
      \linebreak
      \stepcounter{i}
    }
}

\begin{document}
\raggedright
\fibloop{1}{20}
\end{document}

Which generates this:

fib.png

TeX has serious problems with recursion, so we do a loop calculation instead. Oh and while previous code might have implied that \newcounter variables are local - they are all completely global, except for #1 etc. arguments.

Should you use TeX?

Only if you're forced to by your journal. Otherwise no, it's completely obsolete.

Historically its primary strength was math. In particular, supporting made-up mathematical notation each researcher would come up with (the normal notation was supported by everything anyway). But nowadays, other systems can do that too. For everything else, TeX was never good.

TeX also seems to largely abandoning its existing programming language and embedding much saner Lua instead.

Even as an esoteric programming language, TeX's programming language doesn't really do anything too interesting. If you want a fun challenge, writing Postscript is likely to provide a lot better one.

Code

All code examples for the series will be in this repository.

Code for the TeX episode is available here.