19

Here is a discussion of fragile command in LaTeX. And it says that any commands that have an optional argument are fragile, for instance \footnote[2]{myfoottext} and \footnot{myotherfoottext}. I have tried the example below which the commmand \b has an optional parameter but it works without \protect.

\documentclass{article}
\def\b[#1]#2{.#2.\bf #1}
\begin{document}
\tableofcontents
\section{\protect\b[one]two}    %works
\section{\b[one]two}    %also works
\end{document}

Here is the code in the .toc file:

\contentsline {section}{\numberline {1}\b [one]two}{1}% 
\contentsline {section}{\numberline {2}.t.\bf onewo}{1}%

However, it shows the effect of the command \protect .

enter image description here

  • 7
    But the [...]-enclosed argument is not optional, it's just delimited by [...], so it's not fragile. And it won't work if you don't use the [...] argument. – Phelype Oleinik Jun 25 at 14:41
  • So, what's an optional argument? – Brooks Jun 25 at 14:45
  • 3
    TeX doesn't actually know about optional arguments. What is done in LaTeX to get the effect of optional arguments, is to check if the the next token is equal to [ using \futurelet. If this is the case, another macro is called that absorbs a mandatory argument enclosed in [...], similar to your macro. This allows you to change the meaning of a macro depending on whether a certain argument is given or not. – siracusa Jun 25 at 15:01
  • 1
    Note also that you should use \b[one]{two} to have it expand to .two.one instead of .t.onewo (unless that's what you want, of course :-) – Phelype Oleinik Jun 25 at 15:59
29

The command you defined does not take an optional argument, it takes a delimited argument. If you do:

\def\b[#1]#2{.#2.\bf #1}
\b[one]two

it will work fine, however if you remove the [one] TeX will throw an error:

\def\b[#1]#2{.#2.\bf #1}
\b two
! Use of \b doesn't match its definition.
l.5 \b t
        wo
?

because when you define a command with \def\b[#1]#2{.#2.\bf #1}, TeX expects that when you use \b, the input matches exactly the parameter text (i.e., [#1]#2), which means that the next token must be [, and when it is not that error is raised. See here for a brief description of that.

When using just \def none of the arguments are optional! However, let's say you define:

\newcommand\b[2][--empty--]{.#2.\bf #1}

then the command will have 2 arguments, the first of which is optional, and if not given the default value is --empty--. When you use \b, the defined command does not actually take any argument, but it checks if the next character is a [. If it is, the command proceeds to use an "inner" \b (let's call it \b@opt), which is defined as you did, with \def\b@opt[#1]#2{.#2.\bf #1}. However if you use \b without the following [, then a \b@noopt is used, which is defined as \def\b@noopt{\b@opt[--empty--]}. So after all you end up using \b@opt, but the underlying definition provides the optional argument if you don't give it one.

You can manually define that with:

\makeatletter
\def\b{%
  \@ifnextchar[%
    {\b@opt}{\b@noopt}%
}
\def\b@noopt{\b@opt[--empty--]}
\def\b@opt[#1]#2{.#2.\bf #1}
\makeatother

Now, what makes the optional argument thing “fragile”?

A command is fragile when it can't work properly in an expansion-only context, which is usually when being written to a temporary file, like in section headings as you showed, captions, and the like, but also inside an \edef or, more recently, in \expanded.

It's said that commands with optional arguments are fragile because the mechanism that checks if an optional argument is there (precisely, the \@ifnextchar macro above) usually is fragile. It is possible, under some restrictions, to check for an optional argument expandably, like in xparse's \NewExpandableDocumentCommand, but usually that's not the case.

Taking the command defined above as example, if you do \edef\test{\b[one]{two}} (or \write or \expanded) TeX starts expanding from left to right, so the first thing it sees is \b, which is expanded to

\@ifnextchar[{\b@opt}{\b@noopt}

Next the \@ifnextchar test is expanded to:

\let\reserved@d=[%
\def\reserved@a{\b@opt}%
\def\reserved@b{\b@noopt}%
\futurelet\@let@token\@ifnch

Here the problem appears. \let, \def, and \futurelet aren't expandable, so TeX leaves them as they are, and proceeds expanding the rest. All other macros there will be expanded by TeX, but by doing so the \let and \def will not define \reserved@d and such, but their expansion, and this will make the code not work as intended.

Of course this is just an example, but the basic principle of fragility is that a command that contains non-expandable tokens is being used in an expansion-only context.


How to make a command robust?

Until a couple decades ago the only way to make a command robust was to prevent its expansion with \noexpand\command, which makes TeX temporarily treat \command as unexpandable and skip it in an expansion-only context. The downside of this is that as soon as the expansion was carried out the \noexpand would disappear and the command would be fragile again.

To circumvent this LaTeX defines \protect and the accompanying macros \protected@edef and \protected@write, which define \protect as \def\protect{\noexpand\protect\noexpand}. Then, in an expansion-only context \protect\command will expand to \noexpand\protect\noexpand\command. TeX will throw both \noexpands away, temporarily making \protect\command both unexpandable. If you happened to use the command again, it would continue being robust if you used the \protected@... macros instead of the normal ones.

Commands with optional arguments defined with LaTeX2ε's \newcommand and the like have a different look (but the same machinery underneath). If you define \newcommand\b[2][--empty--]{.#2.\bf #1}, then \b will actually be \protected@testopt \b \\b {--empty--} (that \\b is the command \\b, with two backslashes, not \\ then b). \protected@testopt will use the \protect machinery to test whether it can be safely expanded. If it cannot it will leave \protect\b, otherwise it will proceed to use \\b, which contains the actual definition of the command.

All this became easier when ε-TeX introduced the \protected primitive, which allows you to make a macro engine-protected. This means that instead of tricking TeX to \noexpand your macro, you will define the macro as robust with:

\protected\def\b{%
  \@ifnextchar[%
    {\b@opt}{\b@noopt}%
}

and then TeX itself will know that \b is not supposed to be expanded inside an \edef or \write or \expanded, without additional machinery.

LaTeX2ε doesn't use \protected to define robust macros because of backwards compatibility. LaTeX2ε predates ε-TeX, so the protection mechanism was established much earlier. LaTeX3, for example, dropped the 2ε protection mechanism and uses only \protected to define robust macros.


As a side note, I'd change that definition of yours to:

\newcommand\mybold[2][--empty--]{.#2.\textbf{#1}}

and use as:

\mybold[one]{two}

I changed the command to \mybold, as one-letter command names are not generally a good idea. I also changed \bf (which is deprecated for decades now) to \textbf and put the second argument in braces, so that the second argument is two, not just t.

  • 4
    This answer deserves more upvotes! – Steven B. Segletes Jun 25 at 16:31
  • you start off by saying commands defined by \newcommand are fragile but then you correctly explain how such commands are defined in terms of \protected@testopt which as its name implies, internally handles the \protect mechanism, making the defined command robust. – David Carlisle Jun 25 at 18:45
  • @DavidCarlisle Oops... Fixed. Thanks! – Phelype Oleinik Jun 25 at 19:01
11

The information on that page is wrong (or at least outdated, all commands in latex2.09 that had an optional argument were fragile, but latex2e has been available since 1993...)

the example in the question does not define an optional argument but if you change it so that it does, using the facility of \newcommand to define such an argument you will see that the resulting command is robust and this works without error

\documentclass{article}
\newcommand\zb[2][?]{.#2. \textbf{#1}}
\begin{document}
\tableofcontents
\section{\zb[one]{two}}    %works
zzz
\section{\zb{three}}    %also works
zzz
\end{document}

If you look at the .toc file you will see that this did not "blow up" the way a fragile command would, it produces

\contentsline {section}{\numberline {1}\zb [one]{two}}{1}% 
\contentsline {section}{\numberline {2}\zb {three}}{1}% 

Latex defines \zb here in such a way that the \protect mechanism is used internally so you do not need to explicitly use \protect, so such command are, by definition, robust.

Of the list in that page

All commands that have an optional argument are fragile.

As noted above any commands with optional argument defined by \newcommand (as well as some others) are robust (this has always been the case in LaTeX2e)

Environments delimited by \begin ... \end are fragile.

yes (we might fix that one day)

Display math environment delimited by \[ ... \]

No, \[ has been robust since the 2015 release.

Math environment \( ... \) However, $ ... $ is robust

No, \( has been robust since the 2015 release.

Line breaks, \\

No, \\has been robust since the 1994 release

\item commands

Yes.

\footnote commands

Yes.

  • The list fails to mention \linebreak and \nolinebreak, which are still fragile. – egreg Jun 26 at 22:06
  • 1
    @egreg I noticed later that the page was last updated in 1995 so not surprising it has a latex2.09 flavour – David Carlisle Jun 26 at 22:21
  • But it's on the net, so it must be right… – egreg Jun 26 at 22:22
  • @egreg hmm your answers are on the net as well.... – David Carlisle Jun 26 at 22:22
  • @egreg soooorry, my original problem is not the titled ques, it is why optional argument is fragile and what's optional argument. thanks for your suggestion. – Brooks Jul 2 at 15:31

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.