I think one example is that both sin(x) and sin x are valid notation and common to use, and so a function doesn’t seem to be quite right, at least not if we want to allow the markup to mimic the math notation closely.
I see. But then my next question: Why is op not a Typst function, while math.abs is? (And if it is, why isn’t sin
then, which is a special case of op?
With other words, why don’t we implement op as a Typst function and circumvent the problems with that?
op
is a Typst function that you can use to make operators like sin
:
#let mysin = math.op("sin")
$ mysin(x) $
Here mysin(x)
is not a function call, it’s the value mysin
followed by (x)
. But the value mysin
was obtained by calling op
. You could also write
$ op("sin")(x) $
and get the same result.
You can already partially do this by sacrificing one of the symbol accents that works as a function already:
#let sin = sym.circle
#show sin: math.sin
#let accent-char = sin([]).accent
#show math.accent.where(accent: accent-char): it => {
math.sin + $(it.base)$
}
These now render the same,
but the first line is an actual function call.
$ sin(x) \
sin (x)
$
I would also vote for option B for consistency with other similar cases such as x^-1
which is rendered as x^(-) 1
. See also the related issue: Raising to the power of a negative number should not require parenthesis · Issue #5722 · typst/typst · GitHub
Thanks @Y.D.X for the visual summary.
Just to clarify as the case with f_i (x)
shows no representation on the “Next version” side: I guess this pattern will still be supported by the next version and render the same?
this pattern will still be supported
Yes; I omit it because I think f_i (x)
will become rare in the next version. Just likef (x)
today, which I believe most people would write f(x)
instead.
Just a data point. I was also hit by this today as I was translating my brother’s thesis from LaTeX to Typst.
$
"CF"(X, k) &= limits(sum)_(x in X) I_k(x) & quad "Class frequency" \
$
I would thus also be in support for option B.
I’m not a mathematician so I could be wrong, but I support option C or D for the following reasons:
-
Symbols of functions, like f, can be standalone. I mean, when we say there is a function, we don’t have to say f(x). In this case, f is math symbol. And f(x) is a kind of operation to map x to something else via function f. In most cases, attachments are symbols, not operations. Because of this, when we write
$f_i(x)$
, we would not expecti(x)
to be something attached, unlessi
is a user-defined “programming” function (stated as below). In this case, treating thei(x)
part as the attachment seems to be strange. -
Some special functions, like “sin”, they are predefined symbols, although it could be rare to see “sin”, “cos”, etc., alone.
-
However,
abs(x)
represents “|x|” in math. Thus, “abs” itself is not a symbol; instead, “abs(x)” the whole thing is. Becauseabs
is a programming function, not a mathematical symbol or operation. A standaloneabs
does not make mathematical sense. -
For other user-defined functions, I think they would probably prefer keep the whole thing together. As an example, I defined a function to show conditional expectation, like
#let Exp(x, ..args) = $E lr((#x mid(|) #args.pos().join(",")))$
When I write
$e^Exp(x,u)$
, I definitely want the part like $E(x|u)$ to show at the corner, instead of a “E” at the corner but “(x|u)” not.Another example is the Imaginary number. If we define a function
#let Im(a,b) = $#a + #b i$
then I also expect
$e^Im(1,2)$
to show something like “e^(1+2i)”, not “e^1 + 2i”.
Overall, I think it would be better to act differently for different components. If the attached component is a “programming” function, like abs
and user-defined functions, treat the whole thing as the attachment. For math.op
and normal math symbols, don’t show them like function calls.
I don’t know if it makes a difference for your argument, but from $e^Exp(x,u)$
you’d get “e^Exp” followed by “(x, u)”, not “e^E” followed by “(x|u)”; for $e^Im(1,2)$
you’d get “e^Im” followed by “(1,2)”, not “e^1” followed by “+ 2i”.
In other words, the issue isn’t that the function is evaluated and only part of the result becomes the superscript; it’s that option B would not call the function, and put the parameters outside the superscript.
I know I’m a bit late, but I think that an Option F is missing from the suggested solutions: Always greedily match subscripts as far right as possible. That is, f_i(x)
, f_1(2+2)
or g_3(y)
would put everything to the right of _
into the subscript. This eliminates all ambiguity, and lets the user determine this with spaces.
This behavior mirrors what new users encounter with implicit multiplication: Writing ab + x
produces an error, as ab
is parsed as a function. Thus, they have to write a b + x
. In exchange, they get functions without leading delimiters (#
or \
), leading to much cleaner syntax.
The same can be applied for subscripts. In long expressions, reducing parentheses makes code more readable. Letting users control subscripts with x_2(i+1)
or f_2 (i+1)
achieves this.
Cons:
- Requires more spaces in the simple cases such as
f_1 (x)
- For simple subscripts, this contradicts the expected behavior from LaTeX, unlike the proposed solution.
- Is a breaking change, (with wide-spread effect)
Pros:
- Achieves the same unambiguous parsing as the proposed solution
- Allows using spaces as a typographic tool, leading to more readable syntax-code than wide parentheses.
I hope this option can be considered – I think a minimal syntax is a key strength of Typst, and is a goal that should be pursued.
I’m a bit late, but still wanted to add a voice in favor of option F for the reasons presented in above comments. Editing and reading sources with many parentheses is more annoying imo than if we can use spaces (see #6442 (comment)).
Additionally, I would be in favor of making any Unary or Vary operator cause the immediately following element to be scripted as well, so that $e^-x$
would work as expected (and we can still write $e^- x$
if we only want the minus sign to be in the exponent): Raising to the power of a negative number should not require parenthesis · Issue #5722 · typst/typst · GitHub
Some time has passed since I’ve asked for feedback and there were quite a few responses on the PR and here in the Forum. There were some voices for most of the Options (stay as-is, revert to 0.3, runtime-dependant parsing), including for an option not included in my blog post: Basically Option A on steroids, where we expand the current behaviour to numbers (Option F).
If you’re interested in the individual arguments, I invite you to reread the discussion as I can’t fully reproduce everything here. But as a quick summary: The main argument in favor of Option B was predictability while the main argument in favor of Option A and F was terseness, primarily avoidance of additional parentheses, which can make equations harder to read.
There is no way to make everyone happy here, so it’s going to be a difficult decision either way. Personally, I believe that predictable, consistent, and intuitive syntax is more important than a bit of extra tersity (this is also in line with Typst’s generally design philosophy). While I wouldn’t say there is clear consensus, the majority of upvotes on both GitHub and in the Forum seem to share this sentiment. For this reason, I have decided to move forward with Option B.
Thanks to everybody who left feedback, in particular also those that argued for other options.
Has it also been considered to make x^-1
parse as x^(-1)
?
This is something I stumble over again and again, and I feel like there are few cases where people would like to write x^- 1
. But of course it would make the system more inconsistent
Personally, I think making it more inconsistent again for a small gain in brevity would be a step back. That’s how we got ourselves into the whole situation being discussed here in the first place.