Generic grouping syntax like in LaTeX

In LaTeX, you use { } to group expressions, e.g. for attachments. In Typst, you do that with ( ). Understandably, though, ( ) outside of those situations renders as actual parentheses. But in LaTeX, you can also use { } directly in normal equations with no special syntactic role. You can write e.g. 1 + {2 + 3}, which renders like 1 + 2 + 3 would.

However, I find that { } in LaTeX also has nice properties that I’m having trouble finding Typst equivalents for. Unfortunately, I don’t know the actual under-the-hood semantics of { }, so I’m going to describe my mental model here.

In LaTeX, { } will make LaTeX parse the semantic meaning of everything inside the braces separately from everything outside. So, for instance, if you write 1 {+ 2}, the + will be treated as a unary operator even though it’s preceded by another operand. Then, the spacing to the outside of the braces is calculated as if the braces were an ordinary operand, like x or 2.

Some example uses:

  1. Turn an operator into a normal symbol. For instance, some theories will use * as the name of a value, but LaTeX sees it as an operator in many cases. You can write e.g. 3 {*} + 2 to avoid this being parsed as {3} * {+2}.
  2. Avoid something being parsed as a function application, if you e.g. use the convention of writing log x instead of log(x) normally, but still want to group something like log (x + y). log {(x + y)} ensures this isn’t displayed as log(x + y).

As far as I can tell, Typst doesn’t have anything that works like that. I was initially optimistic that #$$ would, e.g., $log #$(x + y)$$. But that doesn’t work.

You can solve case #1 by using class, but using class is always a bit clunky.

Do you think it would be a good idea to add something like this to Typst? Maybe a group function? Or some special syntax? Or does something like this already exist and I missed it? Or do you think it’s not useful at all?

3 Likes

From the examples you posted, you can achieve both with typst already. For turning operators into normal symbols, you can add an escape character before them:

$
3 \* + 2 \       // regular symbol
3 * + 2          // binary operator
$

Then, you can fine-tune things like spacing or the class with show rules:

#show math.equation: it => {
  show "*": text.with(baseline: 0.2em)
  show "*": math.class.with("normal")
  it
}

Operators (like log) are a bit tricky, as using a show rule to add a space looks fine

#show math.op: it => it + math.thin

But it breaks things like attachments, so $log_5 x$ looks off. You can fix this by adjusting the spacing there, too:

#show math.attach: it => {
  show math.op: it => it + h(-math.thin.amount)
  it + math.thin
}
#show math.op: it => it + math.thin

(It “breaks” other function compositions too, but attachments are the most noticable)

I’m not sure if this is the best way of solving this, perhaps someone else can chime in.

On the bright side, (afaik) the “thin” space (or 1/6 em) is the amount of spacing added between operators, so the spacing for $log x$ will not change, but it will add a space before the parentheses $log (x + y)$. The downside is that $log(x + y)$ (without the space after log) will also have spacing around it, but maybe that doesn’t matter in your use case. You can always undo the space by inserting #h(-1em/6) manually.

To address your last point, even thought the way of doing this via show rules is maybe a bit hacky, I (personally) don’t think a new special function/syntax is necessary. I’d assume such functionality would just come with a different set of (opinionated) typesetting rules, rather than the composable show/set rules we have now. I think a better way would be to make show/set rules more powerful by being able to select (or affect) elements conditionally, for example being able to select a class only if it is followed by another class (like to match * in 3 * + 2). There is probably a better way of doing this, but I don’t know enough about typst internals to be able to say.

I’m not familiar enough with LaTeX to know all the ins and outs, certainly not about what curly braces actually do, so if somebody has a more techincal explanation about what they do i’d be happy to hear it.

1 Like

I think class is actually just what you want: it works for any content (not only operators or single symbols) so it does fix your second case too.

Also it seems good to me that the function lets you choose the class, rather than something like { ... } in LaTeX that forces “normal class” I guess. Note that you can easily define a less clunky alias if you want:

#let group = math.class.with("normal")
$
  &log (x+y) \
  &log class("normal", (x+y)) \
  &log group((x+y))
$

So on one hand I agree that something like group would be convenient, but on the other hand I’m not sure it’s worth having it in the standard library when it’s such a shallow addition to math.class.

1 Like

No, this isn’t what I want. class("normal", (x + 2)) will set the normal class on everything inside it, including the +.
What I want is that inside the group, x + 2 is typeset as normal, but the whole unit has spacing around it as if it’s of normal class.

Furthermore, class("normal", ...) is just cumbersome to write and ugly. To me, the fact that math syntax is much more intuitive and less verbose is one of the main advantages Typst has over LaTeX.

$
  & (x + 2) \
  & class("normal", (x + 2)) \
  & log (x + 2) \
  & log class("normal", (x + 2)) \
  & log class("normal", \() x + 2) \
  & log thin (x + 2)
$

Oh I completely missed that! I’m not sure the current behavior is desirable though. It might be a bug (or something to reconsider). Edit: issue filed here.

Certainly more cumbersome than {(x + 2)} but I find it nicely explicit rather than ugly.

1 Like