12

In his answer to a recent question of mine, @egreg advised \newcommand instead of \NewDocumentCommand to define a container for a text. And I'm aware of this (10 years old!) question where:

  • Joseph Wright answers “Conceptually, \NewDocumentCommand is intended for 'package authors' to define commands”. But it is so simpler to define optional arguments (and even starred variants) with \NewDocumentCommand that I don't see why not advise it to document authors.

  • Ulrike Fischer answers “Macros defined with \NewDocumentCommand are robust, they don't get expanded when e.g. moved to the .toc”. But the issue with \NewDocumentCommand example she gave there can be avoided with \NewExpandableDocumentCommand:

      \documentclass{article}
      % \usepackage{xparse}                          % <- useless nowadays
    

    \begin{document} \NewDocumentCommand\testA{}{ABC} \newcommand\testB{ABC} \NewExpandableDocumentCommand{\testC}{}{ABC} % <- added by me

    \tableofcontents

    \RenewDocumentCommand\testA{}{CDE} \renewcommand\testB{CDE} \RenewExpandableDocumentCommand{\testC}{}{CDE} % <- added by me

    \section{\testA, \testB , \testC % <- added by me }

    \end{document}

enter image description here

Hence I wonder why not use:

  • \NewDocumentCommand (and friends) as document author?
  • \NewExpandableDocumentCommand to define a container for a text?

I agree that it wouldn't be ideal to mix \NewExpandableDocumentCommands and \NewDocumentCommands in the same preamble, the former for variables (i.e. container of text i.e. macros used as storage) and the latter for commands. But mixing \newcommands and \NewDocumentCommands would look worse.

Moreover, the arguments in this answer also are in favor of \NewDocumentCommand and \NewExpandableDocumentCommand.

So I know this has been already discussed but, since xparse is now in the kernel, I'd like to know if there are still strong arguments against \NewDocumentCommand and \NewExpandableDocumentCommand for document authors (I'm inclined to promote them in my LaTeX's courses).

Edit

(Here is a bit of context that might explain the current question.)

My concern here is what to teach to beginners or intermediate users of LaTeX. I used to teach \newcommand{\dst}{Dostoïevsky} as an example of a nice feature of LaTeX for a student who has to write a long report on Dostoïevsky. And, of course, I didn't stop there: for students in history, I used to teach:

  • \newcommand{\century}[1]{\textsc{#1}\ieme{}~siècle} (sorry in French, “siècle” being “century” and with the babel-french feature \ieme),
  • and even \newcommand{\century}[2][\ieme]{\textsc{#2}#1~siècle} since, in French, there is an exception only for the 1st century (ignoring negative centuries): \century[\ier]{i}.

And I was after a way to replace this by the more modern, flexible and, I hoped, uniform way of defining variables/commands: the “xparse”' way.

Denis Bitouzé
  • 9,652
  • 4
  • 27
  • 85
  • 2
    The downside of \NewExpandableDocumentCommand for just "container for text" is just the extra overhead (which on any reasonable computer nowadays will only be noticeable if you use it tens of thousands of times) – Phelype Oleinik May 16 '23 at 13:24
  • 4
    the code is longer and tracing errors is more difficult, compare \documentclass{article} \newcommand\testA{blub}\NewExpandableDocumentCommand\testB{}{blub} \begin{document}xxx {\tracingmacros=1 \testA } xxx {\tracingmacros=1 \testB } \end{document} – Ulrike Fischer May 16 '23 at 13:29
  • My advice is to check firstly how expansion works in TeX mechanism. look for \expandafter and \noexpand and such things in The TeXbook and try to create some commands there by \def (just as an alternative and training method of \NewDocumentCommand and \NewExpandableDocumentCommand) – koleygr May 16 '23 at 13:29
  • 1
    BTW, is there any reason for \NewDocumentCommand{\foo}{}{text} (so without any argument) to not behave just as \NewExpandableDocumentCommand{\foo}{}{text}? – Denis Bitouzé May 16 '23 at 13:29
  • @UlrikeFischer Indeed. Isn't it improvable? – Denis Bitouzé May 16 '23 at 13:36
  • @DenisBitouzé how would you determine text was just text and did not have any commands that require protection? – David Carlisle May 16 '23 at 13:47
  • what does improvable mean?? – Ulrike Fischer May 16 '23 at 13:47
  • @DavidCarlisle Indeed. My concern was just to have a uniform way of defining variables and commands and not have to differentiate \NewExpandableDocumentCommand for the former and \NewDocumentCommand for the latter. – Denis Bitouzé May 16 '23 at 19:26
  • @UlrikeFischer I meant to make errors tracing of \NewExpandableDocumentCommand as simple as \newcommand's one. – Denis Bitouzé May 16 '23 at 19:28
  • The best tool for defining macros is \def or \protected\def. I need nothing more. – wipet May 16 '23 at 20:17
  • 2
    I wouldn't define commands like your \dst, this doesn't scale well and doesn't contain semantic information. I would define a command like \people{dst}, which would also have the benefit that spaces are not swallowed, something newbies don't like. – Ulrike Fischer May 16 '23 at 20:21
  • @UlrikeFischer I guess \people would be designed to accept other arguments than just dst. In such a case, its definition would be out of the scope of a LaTeX course for beginners (some of the students are in their first year at university). For courses to intermediate LaTeX users, I insist on semantic information. – Denis Bitouzé May 16 '23 at 20:33
  • 1
    yes that would the plan. But even more importantly it would teach the students that you don't have to fill your preamble with hundreds of definitions that you can barely remember and which only will clash with some package. – Ulrike Fischer May 16 '23 at 20:39
  • 1
    Obviously, this advise to define variables as container of text is only for the main recurring strings of the document. I really don't see why not use this nice feature of LaTeX. And I guess many LaTeX users and packages (ab)use of it. – Denis Bitouzé May 16 '23 at 20:44
  • 1
    @wipet \def is too risky to be explained to beginners and even intermediate users: they might do \def\par{whatever}. – Denis Bitouzé May 17 '23 at 05:33
  • @DenisBitouzé You can declare \def\par{whatever} or \def\fi{finito} without any problems in OpTeX because it uses its internal namespace. But, I understand, now we are talking about LaTeX which includes very old concept without different name spaces. – wipet May 17 '23 at 05:58

3 Answers3

7

If you want a container for text that can be delivered by expansion also in “full expansion” contexts, then \NewDocumentCommand is out of the question, because it defines a \protected command.

So the choice would be between \NewExpandableDocumentCommand and \newcommand. The latter has big advantages: delivering the contained text just requires one expansion step.

Besides, \New(Expandable)DocumentCommand is designed to do parsing of arguments in a cleaner way than with legacy LaTeX programming and not as a catch-all replacement for \newcommand, which will continue to exist. Argumentless macros (that is, containers) are best dealt with \newcommand, if you don't want to follow the path described below.

A better way

Actually, my suggestion is to use neither.

\ExplSyntaxOn
\NewDocumentCommand{\definecontainer}{mm}
 {
  \tl_new:c { l__denis_container_#1_tl }
  \tl_set:cn { l__denis_container_#1_tl } { #2 }
 }
\NewExpandableDocumentCommand{\usecontainer}{m}
 {
  \tl_use:c { l__denis_container_#1_tl }
 }
\ExplSyntaxOff

Or possibly a property list:

\ExplSyntaxOn

\prop_new:N \l_denis_container_prop

\NewDocumentCommand{\definecontainer}{mm} { \prop_put:Nnn \l_denis_container_prop { #1 } { #2 } }

\NewExpandableDocumentCommand{\usecontainer}{m} { \prop_item:Nn \l_denis_container_prop { #1 } } \ExplSyntaxOff

In either case you say \definecontainer{foo}{whatever} and \usecontainer{foo}.

The latter has the advantage that what's delivered by \prop_item:Nn is not subject to further expansion in full expansion contexts.

Real world application

Something like \newcommand{\dst}{Dostoïevsky} seems to simplify one's life, but that's not really true. After a few months, the same author of the essay will not remember what \dst means.

A possible better choice could be to use \dostoievsky as the name, but with the downside that you need {} after it, in order to preserve a following space.

A strategy like the one outlined above might help much better.

% boilerplate code
\ExplSyntaxOn
\prop_new:N \g_denis_names_prop
\NewDocumentCommand{\definename}{mm}
 {
  \prop_gput:Nnn \g_denis_names_prop { #1 } { #2 }
 }
\NewExpandableDocumentCommand{\name}{m}
 {
  \prop_item:Nn \g_denis_names_prop { #1 }
 }
\ExplSyntaxOff

% define names \definename{dostoievski}{Dostoïevsky}

In the body of the document you'd use

The Russian writer \name{dostoievsky} risked to be executed.

The name is there and the obvious convention is to remove capitalization and diacritics. This will be remembered much more easily.

For \century you can do

\documentclass{article}
\usepackage{fix-cm}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}

\ExplSyntaxOn \NewDocumentCommand{\century}{m} { % make the argument lowercase \textsc{\text_lowercase:n{#1}} % add the suffix \denis_century_suffix:n { #1 } } \cs_new_protected:Nn \denis_century_suffix:n { \str_case_e:nnF { \str_foldcase:n { #1 } } { {i}{\ier} } { \ieme } } \ExplSyntaxOff

\begin{document}

\century{i} \century{I} \century{xiv} \century{XIV}

\end{document}

enter image description here

More complicated than using an optional argument? Perhaps, but it makes life much easier.

egreg
  • 1,121,712
  • I see the point but what I'm after is not for so experimented users: I'm after what to teach to newbies or intermediate users. I edited my question (at the end) in order to explain the context. – Denis Bitouzé May 16 '23 at 19:51
  • 1
    @DenisBitouzé What's wrong in using \newcommand? – egreg May 16 '23 at 19:59
  • \newcommand is less flexible: only one optional argument, no direct way to test special values (to be compared to \IfNoValueTF and friends), no easy way to create starred versions of a command, etc. – Denis Bitouzé May 16 '23 at 20:16
  • 2
    @DenisBitouzé Wasn't the question about commands without arguments? – egreg May 16 '23 at 20:20
  • 1
    If I'm right, for commands with arguments, \NewDocumentCommand isn't inadvisable and so I'm inclined to teach it. Then, I would at first glance consider to teach it for commands without arguments as well, since it is just a matter of an empty ⟨arg spec⟩ in \NewDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}. – Denis Bitouzé May 16 '23 at 20:27
  • 2
    About \dst versus \name{dostoievsky}, one point is the former save your time, save your brain (to type this complicated and interminable name), save you from musculoskeletal problems (to type this complicated and interminable name), save you from nervous breakdown because of stupid compilation errors due to misspelling (of this complicated and interminable name). Plus I don't agree “the same author of the essay will not remember what \dst means” is a real issue: that's easy to remember and, if necessary, to retrieve. – Denis Bitouzé May 17 '23 at 05:20
  • As for the definitions of \definename and (yours) of \century, the fact is that they cannot reasonably be explained in a LaTeX course for beginners and intermediate users. – Denis Bitouzé May 17 '23 at 05:20
  • I think \prop_put:Nn in the second code block and \prop_gput:Nn in the third one are missing an additional n as argument specifier. So it should be \prop_put:Nnn and \prop_gput:Nnn, respectively. – EuklidAlexandria Jun 23 '23 at 23:46
  • @EuklidAlexandriaYou’re right – egreg Jun 24 '23 at 06:51
5

In my LaTeX book, I have relegated \newcommand to a sidebar and \NewExpandableDocumentCommand to an appendix, preferring \NewDocumentCommand (and \NewEnvironment) for creating commands.

The biggest limitation had been that tabularray’s expand= option only worked with commands defined with \def or \newcommand¹ but a newer release has relaxed that restriction (and now I need to add a section to the manuscript, having seen this when checking for the name of the option).

I do like to put the argument specification on its own line with \NewDocumentCommand as it makes it harder to miss the empty spec when defining a command without arguments. Compare

\NewDocumentCommand{\dst}
   {}
   {Dostoïevsky}

and

\NewDocumentCommand{\dst}{}{Dostoïevsky}

  1. Except commands defined with \newcommand that have optional arguments.
Don Hosek
  • 14,078
5

We have two cases how to save a tokens list into TeX memory and restore it. First case is defining macros without parameter and second case is using toks registers:

                            save it:  \def\foo{text}    use it:   \foo
or:
declare it: \newtoks\foo    save it:  \foo={text}       use it:   \the\foo

The first case if fully expandable (in context of \edef, \write), the second case expands only to text, but if text includes expandable control sequences then they are not expanded in \edef, \write context. For example

\newtoks\foo  \foo={text: \the\dimen0 }  \edef\bar{... \the\foo ...}

then \bar is a macro with body: ... text: \the\dimen0 ....

Moreover, we can use the prefix \protected before \def\foo in order to prohibit expansion of \foo in the \edef, \write context:

\protected\def\foo{text}  \edef\bar{... \foo ...}

then \bar is a macro with body: ... \foo ....

Note that the prefix \long can be used before \def too but its usage is irrelevant when we define a macro without parameter. Only \ifx differs between long and no-long macros even if they have no parameters.

LaTeX provides huge amount of macros which expand to these primitive constructs (in order to make the world more difficult?). We can try to use them and then to look at the meaning of declared control sequence:

\tt \parindent=0pt \parskip=10pt
\def\foo{text}               def: \meaning\foo     \par
\newtoks\foo  \foo={text}    toks: \meaning\foo    \par
\protected\def\foo{text}     protected def: \meaning\foo   \par

\let\foo=\undefined \newcommand\foo{text} newcommand: \meaning\foo \par \renewcommand\foo{text} renewcommand: \meaning\foo \par

\DeclareRobustCommand\foo{text} DeclareRobustCommand: \meaning\foo \par

\let\foo=\undefined \NewDocumentCommand\foo{}{text} NewDocumentCommand: \meaning\foo \par \let\foo=\undefined \NewExpandableDocumentCommand\foo{}{text} NewExpandableDocumentCommand: \meaning\foo \par

The result is:

defs

Note that macros declared by \DeclareRobustCommand, \NewDocumentCommand and \NewExpandableDocumentCommand don't expand into its declared macro body directly, but there is more complicated way to reach the declared macro body. The declared macro body is invisible when we are using only \meaning\foo. Tracing such macros (using positive \tracingmacros) is very inconvenient.

There are plenty of more macros doing the same or similar things in LaTeX's Expl3 syntax, but I don't want to dig into them. They all run ultimately \def or handle with tokens registers.

wipet
  • 74,238
  • +1 but I disagree on one point: While I am myself no fan of expl3 macros, the fact that \newcommand checks whether the macro name has already been defined isn't a bad idea. So no, not all LaTeX macros are there to make the world more difficult. Only some ;-) – campa May 17 '23 at 09:11