Hacker News new | ask | show | jobs
by akoboldfrying 619 days ago
With your experience of both, have you found that Typst has fewer issues with conflicting/non-commutative plugins than LaTeX does?

Because that's where I lose the most time with LaTeX: packages often mess with the (piles of) global state in ways that sometimes conflict, and the only "solution" seems to be that, if you're very lucky, sometimes conscientious package authors will try to "detect" (through various hacks) whether some other known-conflicting package has already been loaded and adjust accordingly. I didn't see any mention in TFA of any module system or even local variables in Typst to contain this explosion of complexity, so I suspect it will be just as bad in this respect as LaTeX is once there are as many plugins available.

3 comments

I think compatibility issues in LaTeX often come from packages that redefine the same macros in incompatible ways. This kind of things doesn't happen in typst because all user code is pure: a package can define 1) values or pure functions that can be imported (this makes them available only in the scope where they're imported) and 2) content that can be included in the document.

There's still potential for conflicts, for example content can contain elements that represent a state update such as incrementing a counter. Packages can define their own states for internal use, and the namespace is global, so they can interfere with each other if they don't follow good practice (prefixing state names with __package-name for example). And show rules can replace an element of content with another one, for example one package can replace verbatim code with a figure, and another package can format verbatim code. What happens if you mix them without limiting their scope?

But so far, I think the compatibility problems in typst are more of the "well, what do you expect?" kind. Compare to LaTeX where sensible code is broken when a package makes a small changes somewhere deep in the macro pile of cards.

For example someone here mentioned the example of one package changing "the" to red and another changing "the" to blue. This can be done declaratively in typst, and won't cause an error, but the result will depend on the order of declarations.

Thanks for the detailed response.

>compatibility issues in LaTeX often come from packages that redefine the same macros in incompatible ways

Absolutely. A related but more subtle problem occurs when macro A ordinarily calls a macro B, but a package redefines A not to call B at all (perhaps reimplementing part of B itself), and then, when the user includes a second package that redefines B, this latter package appears to have no effect.

Based on your explanation of Typst having a global namespace, I would expect such conventions (like a function A that by convention calls a function B, both of which can be redefined by any package) to arise in Typst just as they have done in LaTeX. (This risk would be much reduced if Typst didn't have functions as first-class values, but from the TFA, I see that it does.)

>Compare to LaTeX where sensible code is broken when a package makes a small changes somewhere deep in the macro pile of cards.

LaTeX has grown "a macro pile of cards" because (a) the base LaTeX system was not comprehensive/expressive enough to let users express everything they wanted to do "within the system" (i.e., by merely twiddling existing knobs in a composable way) and (b) because it is possible (indeed, relatively straightforward, at least initially) to make them.

Maybe Typst has a much more comprehensive and well-designed set of knobs, in which case the conditions will not arise that encourage the same "macro pile of cards" to form. Otherwise, I don't see any reason to expect that it will wind up any different.

The global namespace in typst is limited to "states", a rather specific feature similar to the global TeX counters for headings and equations. So it's possible for two packages to increment the same counter. Everything else is local. All functions and variable definitions are local, and every function is pure: a function call cannot have any side effect. In particular, a package cannot redefine a function.

(You may wonder how functions can be pure if they can increment counters.... The way it works is that you call the function and put the return value in the stream of content of the document. And this value can be something that says "increase counter X by Y". And when you read the counter, typst applies all such "counter instructions" that are in the document so far, and gives you the result.)

The special knobs that typst provides are the "set" and "show" rules. With "set" rules you can change the default values of elements created later in the same scope. For example "#set text(fill: red)" will make it so that all text created later in the same block of code without a specified color will be red. With "show" rules you can define transformations that will be applied to elements created later in the same scope. For example "#show math.equation: it => figure(it, caption: "An equation")" will wrap every equation in a figure with the given caption.

I think the main reason LaTeX is a pile of cards is because TeX is terrible as a general purpose programming language. Just an example: the fact that a macro called with particular arguments will behave differently depending on the current state of catcodes is a craziness that's hard to imagine when you think in terms of "normal" programming. And the whole ecosystem of amazing packages that make LaTeX so useful, they need to do the kind of things you'd normally do in a normal programming language, so these packages end up working around the oddities of TeX in ways that make the whole thing a pile of cards.

With its design based on pure functions, I'd say typst is at the other end of the spectrum in terms of how easy it is to write code that works reliably without interference from other people's code.

I dpn't remember the details, but IIRC in LaTeX I had a problem bacause babel made the > symbol active in s Spanish, and xypic used non-active > to draw arrows.
I imagine you're projecting how LaTeX works onto Typst, though despite years of use and a PhD in PL I never really figured out how LaTeX works so I'm not certain.

I don't think Typst has a lot of global state to get corrupted. Like, if one package defines a variable `foo` and another package defines a variable `foo`, and you use both of them (and don't try to import `foo` from both), it's not like those `foo`s are going to conflict with each other. Is that the sort of issue that LaTeX packages run into?

Likewise, you don't modify typesetting in Typst by modifying global state like you do in Latex. You use `set` and `show`, which are locally scoped. You never need to, like, set the font size, then write some stuff, then remember to set it back. You just put `set font(size)` around precisely the stuff you want to be bigger.

In general, with the TeX "engine", you can locally scope most changes by simply wrapping them in braces {}.

However, if you have a need to override something globally (eg. a global heading font, or spacing at the paragraph level), there is really no way to do it other than doing it globally.

How would Typst solve this without having the same problem? Eg. how can I have a package that sets every "the" in red colour, without it interfering with a package that sets every "the" in blue or titlecase?

Perhaps it's structured better (I would hope so, with ~40 years of learnings since TeX was introduced), but the problem seems unavoidable if you allow things like the above.

>how can I have a package that sets every "the" in red colour, without it interfering with a package that sets every "the" in blue or titlecase?

Exactly.

Broadly, the things you might want a package/plugin to do can be categorised as "local" (e.g., add some new type of content in a fixed-size box that the layout engine can then treat the same way as it treats any other such box) or "global" (e.g., change where floats appear). Making local effects play together can be easily handled with standard PL ideas like lexical scoping, but doing the same for global things is much harder: it strongly depends on exactly what knobs (API) the base typesetting engine provides. We now have design patterns like Observer to help make creating such "global" effects simpler, assuming that their effects are genuinely orthogonal, but what if they aren't?

A plugin that sets each "the" to red clearly conflicts with a plugin that sets each "the" to blue: at most one of them can "win", so which is it? Does it depend on which plugin was loaded first? If so, that's no better than LaTeX, and will become an ever-growing headache as the ecosystem grows.

OTOH, a plugin that sets each "the" in italics can sensibly interoperate with a package that sets each "the" in bold -- and can even produce the same results regardless of which was loaded first, because these effects "commute". These effects could be implemented by having the base engine expose an event that can be listened to by any interested plugin.

ETA: The main reason LaTeX is a pain is because it makes no real attempt (that I can see) to manage any of the inevitable complexity of "global" effects. (Admittedly, this is a tough design problem.) I don't yet see signs of anything better from Typst, thus I assume it will become roughly as painful as LaTeX in time.

I disagree with the assumption that the red/blue conflict should produce an error. In real life most of the time you want one style to override the other. So in this simplistic example at least, having the result determined by the order is the correct behavior (and it's what typst does).

More generally, if your system generates errors left and right, you end up making it hard for users to find a combination of packages that work. It's better to make them work error-free as much as possible. And the concept of "overriding" is natural and useful.

I think typst does make a nice attempt at managing global effects. It's nowhere near perfect but works pretty well already. For example it's super easy to implement your example with two packages, one applying bold and the other applying italic:

    Template from package A: #show regex("\bthe\b"): set text(style: "italic")
    Template from package B: #show regex("\bthe\b"): set text(weight: "bold")
You can use both templates in any order, typst will correctly render "the" in italic bold.
> A plugin that sets each "the" to red clearly conflicts with a plugin that sets each "the" to blue: at most one of them can "win", so which is it? Does it depend on which plugin was loaded first? If so, that's no better than LaTeX, and will become an ever-growing headache as the ecosystem grows.

Just loading a package in Typst won't perform side-effects. Instead what they can do is giving you some function that will apply the styling to any content passed to it. It will be up to you to choose to wrap your whole document in such functions in order have them apply globally, which can be done conveniently with something like `#show: foo_template`, where `foo_template` is the aforementioned function.

This still has a chance of "incompatibility", like in the blue/red example, because you might do this with two functions from two different plugins. However it is up to you to do this, and it will hopefully be clear that you're modifying global styles in two different ways with a specific order between them.

To be fair though I should mention that some packages will expect you to use `#show` with their functions, so sometimes it will be difficult to avoid using it multiple times.

Typst uses pure functions, so they cannot mutate globale state
Set/show rules can modify the state of the top level file, which may as well be global state.
To be honest, I don't know if I've seen conflicting packages.

Do you have specific examples?

There are quite a few examples, but here's two that have affected me

1)pstcol has to be loaded before graphicx (I forget the reason but it just does)

2) if I use pdftricks I have to unset the "clipbox" command because it conflicts with the one in adjustbox.

As you say in another comment, given how latex reports errors debugging these can be a Fun time.

It's been a while, but I vaguely remember that the hyperref LaTeX package for making URLs didn't play nicely with certain other packages -- possibly with one of the citation packages.

Sorry I can't be more specific. I definitely have memories of reordering \usepackage{} statements until "it worked"...

This is correct. In fact there is a highly upvoted question about it on TeX Stack Exchange: <https://tex.stackexchange.com/q/1863>.

See also <https://latex.org/forum/viewtopic.php?t=25156>.