David has explained this one way, I will take a slightly different tack.
First, what is going on? When e-TeX finds a protected macro, it will not expand it inside an \edef
, \write
and some similar circumstances, which usually exhaustively expand everything. That is to ensure you can see the protected token in the result. For example,
\protected\def\a{}
\typeout{%
\a%
b%
}
will show \a b
, even though there is no space between \a
and b
as far as TeX is concerned. That's because the alternative is \ab
, which as we can't see the tokens would be misleading (does it mean \a
followed by b
or a different macro \ab
?).
You can 'force' expansion of a protected macro by using the fact that they do respect \expandafter
:
\protected\def\a{b}
\edef\test{\expandafter\empty\a}
\show\a
using \empty
as something to expand after which will not result in anything remaining behind.
Second, why do you need protection? Some operations in TeX simply will not work within an \edef
as they use TeX primitives which are not 'expandable'. The classic ones here are assignments (\def
, \let
and so on). If you try
\let\a\undefined
\def\b{a}
\edef\test{\let\a\b}
you will not find that \a
ends up defined to give a
. Instead, you'll get an error: in this case 'Undefined control sequence
'. That is because \let
is not expandable. So TeX simply 'leaves it alone' inside the \edef
, and tries to expand \a
. That's not possible, and so an error arises. Thus in general it is not a good idea to try to expand protected macros.
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 \noexpand
s 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
.
Best Answer
While it is possible to list all TeX primitives or all LaTeX kernel commands, there is no list of all possible LaTeX commands as new ones can be added by packages. As such, most people use an approach of searching for the same pattern as TeX does:
\
followed by either one non-letter or one or more letters: this is the pattern for 'document commands'.(It is possible for commands not to start with
\
, but this is used for internal coding and not in documents. Similarly, internal commands often use one or more extra 'letters':@
is traditional while LaTeX3 uses_
and:
. However, for the purposes of highlighting in a document these cases can be ignored.)