Tyst vs LaTeX: Is LaTeX Just Bad?
We're all familiar with LaTeX; an old companion of anyone type setting documents of all kinds, from novel research to university assignments. It's been with us for decades, we know it, we love it. We've built extensive tooling with it, tried new packages left and right, it's the standard for writing mathematics in every editor that can render maths at all.
Why LaTeX kind of sucks
We're used to thinking of LaTeX as a 'type setting' language, built for producing documents to our exacting specifications, and it excels at that task. Nothing before it and few after it have had its flexibility of expression. But another lens to view LaTeX through is that it's a programming language of sorts, which can be run and whose final product is a PDF document. And as a programming language, LaTeX sucks. It has verbose, inconsistent syntax with a steep learning curve. It has abysmally slow compilation times, especially for complex documents (compilation of medium-complexity papers can be multiple minutes!). And don't get me started on the error messages, LaTeX's error messages are hellish to interpret, and to figure out what part of the document they refer to.
A better option?
Typst is a language for type setting documents, which first started life back in ye-olde days of 2019. And it's good- real good. Compilation speeds are lightning fast compared to anything I've ever seen with LaTeX; for simple documents you can even see real time updates, as you type! The error messages are clean and comprehensible, reminding me of Rust in their clarity (likely no coincidence given the Typst compiler is itself a Rust project). But both of these pale in comparison to my favourite feature of Typst. It's legible.
#set text(size: 11pt)
= A heading!
== A subheading!
This is a *normal* paragraph, with $pi * 2 = sin(13)$ some _maths_ in it.
== A simple table!#align(center)[ #table( columns: 7, align: center, table.cell(rowspan: 2)[*Method*], table.cell(colspan: 6)[*Performance (%)*], [*Acc.*], [*Prec.*], [*Rec.*], [*F1*], [*AUC*], [*AP*], [M1], [87.2], [85.4], [88.1], [76.3], [82.1], [80.7], )]
Marvel in its clear syntax!
Even looking at the code for this simple document (and it clearly reads as code!), you can see some of the design inspirations of the language. From the success of simple markup languages like Markdown has been harvested the idea that common actions should be simple, along with the familiar syntax for things like bolding and italicising text. It's taken lessons from every programming language of the last twenty years in how to make a comprehensible scripting language; a clean type system, with optional annotations so that package authors can add them and end users writing a report don't need to bother. It's taken Python's syntax for keyword arguments so that functions can have good defaults and maintain customisability. The documentation is, frankly, stellar. And it has a simple mental model! The only things you need to worry about, the only picture you need to have in your head when reading Typst code, is that it's all secretly functions (and set/show rules to reset function defaults/control when to apply functions). LaTeX's macro system has accreted over the years to have a wide variety of different ways of doing things, all of which have their own specific mental model you need to get, to be able to use them. Typst, on the other hand? All just functions.
Some demonstratory examples
But to truly get a sense of how Typst improves on LaTeX we'll have to put them to work.
Tables; how bad can they be?

This is a simple table, right? There's not much happening here at all, you could throw this together in less than a minute using Excel. Surely the LaTeX code to render it can't be that bad?
The table from hell
\begin{table}[ht]
\centering
\begin{tabular}{|c|c|c|c|c|c|c|}
\hline
\multirow{2}{*}{\textbf{Method}} & \multicolumn{6}{c|}{\textbf{Performance (\%)}} \\
\cline{2-7}
& \textbf{Acc.} & \textbf{Prec.} & \textbf{Rec.} & \textbf{F1} & \textbf{AUC} & \textbf{AP} \\
\hline
M1 & 87.2 & 85.4 & 88.1 & 76.3 & 82.1 & 80.7 \\
\end{tabular}
\end{table}
What is this?? A mess, that's what this is. Those multirow, multicolumn commands are painful to parse, the \textbf
everywhere is such clutter, and you have to specify where the table lines should be.
A fairly nice table!
#table( columns: 7, align: center, table.cell(rowspan: 2)[*Method*], table.cell(colspan: 6)[*Performance (%)*], [*Acc.*], [*Prec.*], [*Rec.*], [*F1*], [*AUC*], [*AP*], [M1], [87.2], [85.4], [88.1], [76.3], [82.1], [80.7],)
Now this, in comparison, is pretty nice! The number of columns is clearly legibly, the number of arcane symbols has gone down, and there's so much less clutter. It's clean, elegant, and readable.
Maths!

Here's some gibberish equations cramming in a bunch of symbols! Let us compare once again:
Maths Formula Nightmare
\begin{align*}
\hat{R}(\theta)
&= \frac{1}{n}\sum_{i=1}^{n}\left[y_i - f_{\theta}(x_i)\right]^2 + \lambda\|\theta\|_2^2 \\
&= \frac{1}{n}\left\| y - \Phi\theta \right\|_2^2 + \lambda\|\theta\|_2^2
\end{align*}
Truly terrible. I mean, we've dealt with it for so long that it feels unmentionable but \frac
? Five characters for a fraction that has nothing to do with the visual representation we're all so well trained on? We've known for ages that prefix notation is a bad choice for legibility, even the most die hard of functional languages lets you use infix notation for divides. The whole thing is just long, cumbersome, and has terrible defaults (I mean, what fraction of the time do you want the brackets to not scale to fit their contents? \left
and right
should be the default.)
Legible equations!
$
hat(R)(theta)
&= 1/n sum_(i=1)^n [y_i - f_theta (x_i)]^2 + lambda ||theta||_2^2 \
&= 1/n ||y - Phi theta||_2^2 + lambda ||theta||_2^2
$
This is soo much better. The fraction intuitively reads as a fraction. The norms intuitively read as norms. There aren't excessive backslashes everywhere creating purposeless visual noise. The equations actually fit on one line.
A tangent to explore cute maths
This isn't especially relevant to my point but I felt compelled to show you my favourite cute, useful maths shortcuts that Typst gives us.
I just think oo
for an infinity design is so cute, and all of these shortcuts really go a long way towards making complicated mathematics more readable.
Custom Command Madness
Alright, back to the horrors. This one is truly, truly cursed.
\newenvironment{rSection}[2]{
{\large\noindent\ignorespaces \textbf{#1}}
\ifthenelse{\equal{\detokenize{#1}}{\detokenize{PROJECTS}}}
{\href{https://#2}{\large\faGithub \textbf{ GitHub:\hspace{2pt}#2}}} % TRUE
{{}} % FALSE
\ifthenelse{\equal{\detokenize{#1}}{\detokenize{INTERESTS}}}
{\href{https://#2}{\large\textbf{#2}}} % TRUE
{{}} % FALSE
\large\noindent\ignorespaces
}{\noindent\ignorespacesafterend}
This is a command from an actual GitHub repository; I searched for anything using \ifthenelse
, on the assumption that if this command is present you know the result is going to be illegible. This is a command that creates a section which is either a 'project' or an 'interest' and titles it accordingly, then constructs and inserts the correct http
link. Notice once again the cursed, illegible syntax. Consider, the use of #1
and #2
to refer to the two input variables to the environment macro. Think about how long it would take you to figure out what this code does when coming across it for the first time, and how it makes you feel.
#let r-section(type: str, address: str, body) = [ #set par(first-line-indent: 0em)
#text(size: 12pt)[*#type*] #if type == "PROJECTS" { link("https://" + address)[#faGithub[*GitHub: #h(2pt)#address*]] } else if type == "INTERESTS" { link("https://" + address)[*#address*] }
#body]
And then consider the alternative. Defining a custom environment is just defining a function that accepts a body! What each line does is made clear by the power of comprehensible variable names, and a syntax familiar to anyone who has touched any major programming language ever. What does an if expression look like? It looks like that. That is the standard way to do an if expression.
Templates
This is the final feature that I'll touch on, and it's one of the most damning. How many of you have written LaTeX? And of those, how many have ever written a LaTeX template? Much, much fewer. I've been using LaTeX for years and I've never bothered figuring out how to write a template, because it's unintuitive, and requires a deep understanding of the TeX rendering system that almost no-one has. In Typst, templates are- you guessed it- just functions.
#let template(doc, author: str) = [ #set text(size: 15pt, font: "DejaVu Math TeX Gyre") #set page( paper: "a4", header: align(right)[ #author ] )
#show heading: it => [ #set align(center) #set text(20pt, weight: "regular") #block(smallcaps(it.body)) ]
#doc]
This is a stupid simple template, but you can already see how it's doing interesting things (and get a pretty good guess at what those things are!). It's setting the default text size and font across the whole document that it's applied to. It's setting the page size, and adding a header which contains the author parameter. It's changing how every heading in the whole doc is rendered by applying an arbitrary function to them(!), namely one that centers them, sets their text size, and renders them with a small-caps font. You can see how this same kind of stylistic control can be used to easily control every aspect of a document's rendering; and how you can build it.
#show: template.with(author: "A cool Author")
This document will now be on a4 paper, and my dramatic authorial title displayed proudly on every page.
= Heading
plus, all of the headers will be just how we designed them.
And applying such a template to your documents is as trivial as can be. Just a show command, same as any other formatting application. You want to set some of the arguments? Do it with with
.
A riveting conclusion
If this has inspired your interest, you can learn more about the Typst project here (Or check out its magnificent docs). If it's instead inspired your curiosity as to what I'll say next, welcome! I'm planning on writing more articles on weird, niche tech that I find (as well as whatever else strikes my fancy), but make no promises that I'll actually do it. Life gets busy, sometimes. If I've failed to convince you, and you hated my writing, hi? What are you still doing here? I'd love to know.
My kind regards to all who've made it this far, and I look forward to sharing more of my explorations with you.