There are some issues with TeX. Not that it's a expansion language.
-
It has a few quirks in behavior that will take you hours of debugging if you don't know them in advance.
Example
- macros - Why does a newline character still disappear even with other/active catcode set? - TeX - LaTeX Stack Exchange – remember to always set
endlinechar
in verbatim-like environments. - tex core - How to use \errorcontextlines interactively? - TeX - LaTeX Stack Exchange – remember to terminate TeX-style assignments with
\relax
, a space/newline, or~
in expl3.
It may be worth it to spend a few hours reading the TeXbook the first time, that may help you avoiding the issues.
- macros - Why does a newline character still disappear even with other/active catcode set? - TeX - LaTeX Stack Exchange – remember to always set
-
It uses "global variables/states" extensively.
This is actually not an issue (in "normal" cases it greatly shorten code), but for complex tasks the user need to understand much of the details how TeX works to make modifications.
-
Not everything is expandable.
This doesn't make TeX hard, but it does make TeX tedious, in that
-
It's not possible to parse TeX source code without executing it.
Combined with the fact that by default TeX grabs arguments unexpandably, this is a huge problem.
(as mentioned below, it does not make tasks impossible, but it does make it difficult to write correct code that handles all corner cases.)
Surprisingly, that it is a macro expansion language is not really the issue.
I don't know Lisp, but I know some similar Lisp-like languages with the "code is data" concept. (of course, the comparison isn't really fair as the other languages aren't used to typeset text, and definitely not for typesetting text in a way that makes the source code concise)
Consider Mathematica:
-
Variables and functions are the same thing.
-
It can do expansion before or after assignment.
Compare
a = b
(evaluateb
, then inject the result intoa
) anda := b
(definea
to beb
).In some respect,
=
is similar to TeX'sedef
and:=
is similar to TeX'sdef
.The large difference is that the evaluation value of
b
in the first case is identical to that if it were evaluated independently; while in TeX it's evaluated in an expansion-only context.Actually, if you think about it, TeX has a good (and very simple) reason to make some things not expandable by default: normally you don't want to do the typesetting work when you assign/pass the result to something else.
Consider
\edef \a {\hbox { \b }}
. In "typical" cases you want\b
to be expanded and\hbox
to not be expanded.Similarly
\edef \a {the section is \the\c@section}
(normally you want to define\a
asthe section is 1
, not to typeset the text then define\a
to be empty.TeX does provide a primitive
\noexpand
to temporarily prevent expansion, but there's nothing that "temporarily allow executable things to expand". (until LuaTeX has\immediateassignment
, but things like hbox have to wait)Remember that Knuth never intend anyone to do "complex programming tasks" in TeX, only as a typesetting engine, so it was not designed for the former purpose.
-
Functions can either choose to evaluate the arguments or not.
In TeX, all functions does not pre-evaluated the argument, but it's possible to define variants that x-expand the argument.
Unfortunately compared to Mathematica, TeX cannot evaluate everything so it isn't really possible to nest things comfortably. (have to store intermediate result into variables)
-
Inject evaluated value into expression is "nontrivial".
It even has a name "Trott-Strzebonski in-place evaluation technique": evaluation - Replacement inside held expression - Mathematica Stack Exchange
Surprisingly, this feature is not directly supported by the kernel, it uses some quirk of the way the kernel evaluates code.
-
It has special syntaxes ("verbatim" – actually just string, etc.).
This one Mathematica is much better than TeX that it parses the whole thing in advance, so there's no annoying catcode bug.
On the other hand it's not possible for functions to define custom input format, but since it's possible to pass string to functions and Mathematica core syntax is already very expressive this isn't a particularly important issue.
-
It has
Block
to "temporarily backup" value of variables.This is similar to TeX's grouping mechanism, but it can "selectively" backup and restore.
Note that Mathematica is also macro-expand-only (it does not have real functions!), but it doesn't matter because
- Usually you don't have to be concerned with "one-step expansion"
- There's no distinction between expansion and execution.
-
It has
Module
to create a closure-like environment.This is like TeX's manual prefixing of commands, but the TeX way is more tedious.
-
Nonlocal error messages
This one I don't know, I hardly encounter errors in Mathematica if I recalled correctly.
Although the truly "hard to understand" error messages in TeX are the ones concerning the syntax of TeX, so with a cleaner syntax it would not happen at all.
Example:
This one you will encounter something like "Missing
{
inserted", and you need to understand how^
works (in the processing engine) to understand what it does. -
While it's possible to implement a stack for saving and restoring values like TeX's
\begingroup
...\endgroup
(which is roughly similar toBlock[...]
in Mathematica), it's not a built-in functionality, so there would be no ambiguity like "what should\begingroup
do".
-
The fact that some things are not expandable makes source code longer.
For the same reason why assembly is harder to read than C (you have to store all temporary values into registers, and recall the same register later), that if you want to use temporary values in TeX that requires non-expandable computations,
-
It's very space-sensitive, thus programmers tend to squeeze code together.
(fixed in expl3 programming environment)
This actually isn't that bad, but the fact that both the backslash and the control sequence name is highlighted with the same color on my text editor makes it extra bad.
Compare:
if x is equal to 0: print the value of x \if\x\is\equ@l\to\zer@: \print\the\v@lue\of\x ifzxziszequalztoz0: printzthezvaluezofzx
The backslash is "halfway" dense between normal character and the
@
, so it doesn't really stand out. In comparison, the blank space is very light. -
It has no local scope, so there's a lot of module-prefix clutter on the screen.
Actually, in
dtx
files, the unique prefix is represented by something like@@
which is translated to the correct prefix while "compiled" (or so I think?) so maybe it isn't that bad.
By design, LaTeX is designed so that tasks it supports is easy, and tasks it does not support is hard.
Although LaTeX is not a single thing, it's a collection of packages written by several people.
Example:
It does that by providing packages, so "in theory" you only need to learn the interface to use the packages; but in reality, the way TeX works make writing correct package code difficult.
Example Square brackets in listings caption - TeX - LaTeX Stack Exchange
To understand how to "fix the issue" it may be necessary to understand how TeX works in low level and/or read the source code of the packages, which is not always easy.
Reference: