Short theory
\cs_new_protected:Nn
is documented in the interface3.pdf
and states that a function defined with it doesn't expand any further inside of an x
or e
type expansion.
What this means at the lower level is, that it uses the e-TeX primitive \protected
. You can see what that means in the e-TeX manual (texdoc etex
). But to summarise:
A macro defined \protected
- doesn't expand in an
\edef
or \expanded
context
- doesn't expand in a
\write
or \message
context
- stops TeX's scanning for
\omit
or \noalign
inside of an alignment context (like a \relax
would)
Simple examples
An example showcasing the different behaviour (in plain TeX):
\protected\def\foo{protected }
\def\bar{unprotected}
\edef\baz{\foo\bar}
\show\baz
\bye
Results in the following output to stdout:
This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023) (preloaded format=pdflatex 2023.8.16) 29 SEP 2023 12:21
entering extended mode
restricted \write18 enabled.
%&-line parsing enabled.
**document
(./document.tex
LaTeX2e <2023-06-01> patch level 1
L3 programming layer <2023-08-11>
> \baz=macro:
->\foo unprotected.
l.4 \show\baz
?
As you can see \bar
was expanded (to the word "unprotected"), but \foo
wasn't and remains as \foo
inside of \baz
's replacement text.
Another example using the L3 syntax and printing into the PDF:
\documentclass{article}
\begin{document}
\ExplSyntaxOn
\cs_new_protected:Nn \test_foo: { foo }
\cs_new:Nn \test_bar: { bar }
\cs_new:Nx \test_baz: { \test_foo: \test_bar: }
\test_baz: \par
\cs_set_protected:Nn \test_foo: { FOO }
\cs_set:Nn \test_bar: { BAR }
\test_baz:
\end{document}

As you can see, on the first usage of \test_baz:
it printed "foobar" since both \test_foo:
and \test_bar:
were lowercase. If we alter that afterwards to both being uppercase this only results in "FOO" but "bar" remains lowercase. That's the case because when we defined \test_baz:
we expanded \test_bar:
to "bar", but \test_foo:
wasn't expanded due to it being protected
, so its redefinition also alters the result of \test_baz:
.
Why or when is this behaviour important?
In TeX we have the notion of things that are expandable, and things that aren't. Some primitives are expandable, but most aren't. Macros on the other hand are expandable (unless defined \protected
, in which case they are only expandable by certain operations like \expandafter
, but not in \edef
or \expanded
).
If a macro now uses one of the unexpandable primitives, but somehow ends up in an \edef
or \expanded
context and would expand itself you'd get undefined/unpredictable behaviour (because anything after your unexpandable primitives still gets expanded). Because of this, it is necessary to define such macros \protected
, so that the behaviour (almost -- let's ignore the ways to still expand a \protected
macro) always stays well defined.
Here is a small, hopefully easy to understand example of this (again in plain TeX syntax):
\def\mymacroA{\def\mythingA{foobar}}
\protected\def\mymacroB{\def\mythingB{foobar}}
\edef\test{\mymacroB}% this is still fine
\edef\test{\mymacroA}% this throws an error
\bye
This prints to stdout:
! Undefined control sequence.
\mymacroA ->\def \mythingA
{foobar}
l.5 \edef\test{\mymacroA
}% this throws an error
?
even though both \mythingA
and \mythingB
would be fine if used outside of \edef
. The reason is quite simple: \def
isn't expandable, so stays the same, but TeX continues to expand the rest of the replacement text of \mymacroA
which hits \mythingA
. That macro isn't expandable, so we get above error message.
\cs_new_protected:Nn
defines a\protected
macro, take a look at the e-TeX manual what that means (it basically means that the macro doesn't expand in an\edef
context, stops the scanning for\noalign
or\omit
in an alignment, doesn't expand inside a\write
)