6

I want to define a macro \ind which let's me set in math mode a subscript in upright letters and when starred (\ind*) in italic letters. Additionally, an optional argument allows me to adjust the kerning between letter and index, i.e. W_{\ind[-2mu]{x}}. This works quite well. But surprisingly, I have to put braces around the \ind-command in order to make it run.

Can someone explain me why I have to put braces around the command and how I can modify the macro to avoid this? It's a super big deal but I would like to understand the mechanism behind it.

The error is always that a { and a } is missing.

MWE:

\documentclass{article}

\makeatletter
\newcommand\ind{\@ifstar{\ind@star}{\ind@nostar}}
\newcommand\ind@star[2][]{\mkern \muexpr 0mu #1  #2}
\newcommand\ind@nostar[2][]{\mathrm{\mkern \muexpr 0mu #1  #2}}
\makeatother

\begin{document}
    $\sigma_{\ind{xy}}$  % works
    $\sigma_\ind{xy}$    % does not work
\end{document}
  • 2
    Welcome to TeX.SX! Commands that do work without braces, like \text are really an exception to the rule. The proper LaTeX syntax requires braces: if something works without them it's by accident. What makes it not work in your case is the \@ifstar test. – Phelype Oleinik Aug 1 at 0:07
  • IIRC things like \def\ind#1{ { do something with #1 } } works. So perhaps \newcommand\ind[1]{ { \@ifstar \ind@star \ind@nostar #1 } } works. – Symbol 1 Aug 1 at 0:20
  • @Symbol1 It would only if none of the optional arguments were given. Otherwise the argument #1 would be a * or a [ and the command wouldn't work properly. – Phelype Oleinik Aug 1 at 1:02
  • 1
    Ooooof yeah you are right. Let's me not having enough sleep. – Symbol 1 Aug 1 at 2:13
  • @PhelypeOleinik Do you mean \sigma_\text{xy} works? That's really unexpected. How come? – Hagen von Eitzen Aug 1 at 16:18
11

Well they are missing. Not to worry, though, you can easily add them since you don't need the literal brace characters used for delimiting macro/command arguments, but can use \bgroup and \egroup.

\makeatletter
\newcommand\ind{\bgroup\@ifstar{\ind@star}{\ind@nostar}}
\newcommand\ind@star[2][]{\mkern \muexpr 0mu #1  #2\egroup}
\newcommand\ind@nostar[2][]{\mathrm{\mkern \muexpr 0mu #1  #2}\egroup}
\makeatother

At some future time, or with some unknown package, the subscript and superscript signal characters (_ and ^) could conceivably be implemented as "active" character commands that take arguments, in which case explicit brace grouping would be required.

| improve this answer | |
  • 1
    I tried \bgroup and \egroup but apparently not at the correct positions. Thanks! – Marc Krass Aug 1 at 10:01
11

Subscripts (and superscripts—what I say here is valid for both) in TeX only work without braces for single tokens (or some special low-level cases like \char<number>; not really LaTeX syntax), so you can write 2^n rather than the comparatively long 2^{n}. If next unexpandable token (except \relax) after a superscript character is a { (or other begin-group character token), then TeX takes a balanced list of tokens for the superscript. TeX also expands tokens after a superscript, so you can define shorthands like \def\funny#1{{#1+n}} then write 2^\funny{1} (\funny{1} expands to {1+n} and the braces are there).

However some commands don't expand properly to a neatly braced list of tokens like \funny or \mathrm do: they are designed to work in subscripts without surrounding braces by expanding to a braced list of tokens. In your command, the first unexpandable token is a \let (in the definition of \@ifnextchar, in the definition of \@ifstar) so when TeX sees it, it knows that a \let alone doesn't work and tells you that you forgot a { at that point:

! Missing { inserted.
<to be read again> 
                   \let 
l.16     $\sigma_\ind
                     {xy}$    % does not work
?

If you are bold, you can ignore this error and the next one. TeX will try to recover and in this one particular situation it will add the two missing braces, making both lines essentially the same.

Use braces: that's the proper syntax!


That said :-)

With xparse you can define a version of that command that looks for optional arguments expandably, then adds the missing braces in the definition:

\documentclass{article}

\usepackage{xparse}
\makeatletter
\NewExpandableDocumentCommand \ind { s O{} m}
  {{% <- extra set of braces
    \IfBooleanF{#1}{\mathrm}% If no star argument
      {\mkern \muexpr 0mu #2 #3}%
  }}
\makeatother

\begin{document}
    $\sigma_{\ind{xy}}$     % works
    $\sigma_\ind{xy}$       % works
    $\sigma_\ind[-5mu]{xy}$ % also works
\end{document}

enter image description here

| improve this answer | |
  • 1
    It's not actually single tokens or explicitly braced material for superscripts and subscripts -- that's the rule for macro arguments. A sub/superscript is a <math field> which consists of filler followed by one of: a character (including \char commands), or a math character (includes \matchar and \delimiter commands), or math mode material in braces (either {} or \bgroup\egroup) . So $ z_ \relax \mathchar121$ gives an upright y subscript. – Donald Arseneau Aug 1 at 9:23
  • Your answer is very useful. In both ways understanding and circumventing the problem. But I like Donald's solution a bit more since it works without an additional package. – Marc Krass Aug 1 at 10:04
  • 1
    I also like the demonstration of an expandable command with optional arguments in which one can use explicit braces! – Donald Arseneau Aug 1 at 10:17
  • @DonaldArseneau It seems I oversimplified a bit. Thanks for pointing it out! :-) – Phelype Oleinik Aug 1 at 14:44
  • @MarcKrass No problem; glad you liked. Just for the record, starting with the 2020-10-01 release of LaTeX, xparse will be loaded in the LaTeX kernel (If you have TeXLive 2020 or MiKTeX you can already test that by using pdflatex-dev; see latex-project.org/news/2020/07/22/latex-dev-2020-10-1b) – Phelype Oleinik Aug 1 at 14:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.