4

According to the documentation of the package xparse the syntax of \NewExpandableDocumentCommand is:

\NewExpandableDocumentCommand ⟨function⟩ {⟨arg spec⟩} {⟨code⟩}

When using xparse's \NewExpandableDocumentCommand for defining an expandable command ⟨function⟩ - what is the exact rule for calculating the amount of expansion-steps it takes outgoing from ⟨function⟩⟨arguments⟩ to obtain the ⟨replacement-text⟩ which is formed by the tokens ⟨code⟩ with ⟨code⟩'s parameters #1, #2, etc replaced by the ⟨arguments⟩?

E.g., using \newcommand one can do

% LaTeX 2e
\newcommand\FirstAndSecond[2]{#1 and #2}
\expandafter\def\expandafter\test\expandafter{\FirstAndSecond{A}{B}}
\show\test
\stop

and as expected with one \expandafter-chain one gets the message:

> \test=macro:
->A and B.
l.4 \show\test

If one uses \NewExpandableDocumentCommand instead of \newcommand and does

% LaTeX 2e
\RequirePackage{xparse}
\NewExpandableDocumentCommand\FirstAndSecond{mm}{#1 and #2}
\expandafter\def\expandafter\test\expandafter{\FirstAndSecond{A}{B}}
\show\test
\stop

, then one gets the message

> \test=macro:
->\__xparse_start_expandable:nNNNNn {mm}\FirstAndSecond  \FirstAndSecond  \Firs
tAndSecond code ?{\__xparse_expandable_grab_m:w \__xparse_expandable_grab_m:w }
{A}{B}.
l.5 \show\test

. Obviously commands defined in terms of xparse's \NewExpandableDocumentCommand need more expansion-steps.

It seems using \romannumeral-expansion one can work around this without knowing the exact rules:

% LaTeX 2e
\RequirePackage{xparse}
\csname @ifdefinable\endcsname\stopromannumeral{\chardef\stopromannumeral=`\^^00}%
% !!! The call to \FirstAndSecond must always be preceded by \romannumeral !!!
\NewExpandableDocumentCommand\FirstAndSecond{mm}{\stopromannumeral #1 and #2}
\expandafter\def\expandafter\test\expandafter{\romannumeral\FirstAndSecond{A}{B}}
\show\test
\stop
> \test=macro:
->A and B.
l.7 \show\test

But this question is not about weird workarounds but about exact rules. :-)

13
  • 1
    'No idea' - we really don't want people worrying about such things beyond low-level code (so definitely not for document commands). I'd also point out that \expanded makes such concerns basically irrelevant.
    – Joseph Wright
    Jul 13, 2021 at 13:29
  • 1
    @UlrichDiez Actually, with the current implementation, you can't know the number of expansion steps reliably. Using code from tex.stackexchange.com/a/492956/134574 I counted 4 steps for initialisation and 16 for cleaning up, plus 3 steps per m argument in your example. However, if I change your example to om, the function requires a different amount of steps if used as \FirstAndSecond[A]{B} or \FirstAndSecond[[A]]{B} or \FirstAndSecond{B} or ... because it processes what it sees differently. Jul 13, 2021 at 14:03
  • 1
    @UlrichDiez No, of course normal users don't need to. But they also shouldn't be relying on detailed expansion behaviour of document commands. As it is, having to have an expandable/non-expandable split is not brilliant, but it's required (at least, unless we switch a lot of stuff around and perhaps require LuaTeX).
    – Joseph Wright
    Jul 13, 2021 at 15:46
  • 2
    I wonder if there's a concrete use case that would illustrate the need to know expansion detail: it's certainly possible to make it more tightly defined, but I think that would require a definite reason to change the current code.
    – Joseph Wright
    Jul 13, 2021 at 16:13
  • 2
    @UlrichDiez If one macro depends on the number of expansion steps of another document level macro that would indicate very tight coupling which makes it very hard to modify anything about the involved macros. If the expandable macro generates values which should not get expanded further, it can just use \unexpanded around it to allow reuse without depending on implementation details. Jul 14, 2021 at 18:46

0

You must log in to answer this question.

Browse other questions tagged .