2

Is there way to expand the body passed to NewDocumentCommand at the point of definition? Ideally, just expanded the once.

Here is my (minimalised) scenario.

\documentclass{minimal}

\newcommand*{\baz}{\foo}
\newcommand*{\expandme}{good}

\ExplSyntaxOn
\exp_args:NV \NewDocumentCommand \baz { s D<>{} } {
    \expandme% Should get expanded once at definition time.
}
\ExplSyntaxOff

\renewcommand*{\expandme}{bad}

\begin{document}

    \foo<bar>% Prints "bad", but I want it to print "good".

\end{document}

Making the following modification yields an Undefined control sequence error, presumably because \exp_args is having a hard time with the { s D<>{} } argument.

\exp_args:NVNo \NewDocumentCommand \baz { s D<>{} } {
    \expandme% Should get expanded once at definition time.
}

Finally, note that the following solution, which works in the simplest case, but is problematic when \bar can take a number of values, and this code is repeated for each (as is my real scenario).

\exp_args:NNV \cs_set:Npn \tmp \expandme
% alternatively to the above line, I think: \edef\tmp\expandme
\exp_args:NV \NewDocumentCommand \baz { s D<>{} } {
    \tmp
}
3
  • { s D<>{} } needs to be n not N Sep 11 '21 at 18:02
  • @DavidCarlisle Sorry, I forgot to adjust that bit of code while testing. Don’t help though.
    – Noldorin
    Sep 11 '21 at 18:06
  • Doesn't help though. *
    – Noldorin
    Sep 11 '21 at 18:56
4

You can use \expanded in recent tex releases:

\documentclass{minimal}

\newcommand*{\baz}{\foo}
\newcommand*{\expandme}{good}

\ExplSyntaxOn
\expanded{\noexpand\NewDocumentCommand\expandafter\noexpand\baz { s D<>{} } {
    \unexpanded\expandafter{\expandme}% Should get expanded once at definition time.
}}
\ExplSyntaxOff

\renewcommand*{\expandme}{bad}

\begin{document}

    \foo<bar>% Prints "bad", but I want it to print "good".

\end{document}

Or you can package it up as a custom VnV argument list

\documentclass{minimal}

\newcommand*{\baz}{\foo}
\newcommand*{\expandme}{good}

\ExplSyntaxOn
\exp_args_generate:n{VnV}
\exp_args:NVnV\NewDocumentCommand\baz { s D<>{} } \expandme
\ExplSyntaxOff

\renewcommand*{\expandme}{bad}

\begin{document}

    \foo<bar>% Prints "bad", but I want it to print "good".

\end{document}
6
  • @Skillmon I updated Sep 11 '21 at 18:33
  • Two interesting solutions -- thank you. I didn't know about \expanded or \unexpanded, but they seem handy. In this case, I find the the former solution to be clever but took me a few minutes to understand the first time. The second solution seems more elegant and just as good, as long as one is familiar with expl3 syntax. I presume \cs_new:Npn \exp_args:NVnV {\::V \::n \::V \::: } specifies the types of the parameters in reverse due to associativity?
    – Noldorin
    Sep 11 '21 at 19:35
  • 1
    @Noldorin \unexpanded is standard etex so has been available for decades, \expanded came from a never distributed pdftex branch that was picked up in luatex so was luatex only until last year when it was back ported to all the texlive engines so pdftex, xetex, ptex as well as luatex Sep 11 '21 at 19:38
  • \exp_args_generate:n is in the kernel for a reason…
    – egreg
    Sep 11 '21 at 20:34
  • @egreg done.... Sep 11 '21 at 22:43
3

If for some reason the needed \exp_args:N... variant isn't available, you can help yourself with \use:n:

\documentclass{minimal}

\newcommand*{\baz}{\foo}
\newcommand*{\expandme}{good}

\ExplSyntaxOn
\exp_args:Nno \use:n { \exp_args:NV \NewDocumentCommand \baz { s D<>{} } } {
    \expandme% Should get expanded once at definition time.
}
\ExplSyntaxOff

\renewcommand*{\expandme}{bad}

\begin{document}

    \foo<bar>% Prints "bad", but I want it to print "good".

\end{document}
10
  • Ah, this is very useful to know about \use:n and how it can serve in such situations. Thank you.
    – Noldorin
    Sep 11 '21 at 19:31
  • @Noldorin it's a trick I often use when defining expl3 functions at low-level code. For example I might use something like \exp_args:Nno \use:n { \cs_new:Npn \module_function:w #1#2\foo\bar } { \__module_tmp: }.
    – Skillmon
    Sep 11 '21 at 19:35
  • 1
    @Noldorin except that the \:: macros are documented as "shouldn't be used outside this module" no, not really. The pseudo-code above just does one step of expansion for the definition of \module_function:w, which has a long argument list, so using \exp_args:N... becomes more or less impossible.
    – Skillmon
    Sep 11 '21 at 19:43
  • 2
    There is \exp_args_generate:n
    – egreg
    Sep 11 '21 at 20:33
  • 2
    @egreg sure, let's go and do \exp_args_generate:n { NNNNNNNNNo }... Just to use it one single time for something that could be done much easier, in an easier to maintain way. I agree that there are variants for which \exp_args_generate:n is the answer, but it's not always the answer.
    – Skillmon
    Sep 11 '21 at 21:08
3

You should define an interface for this.

\documentclass{article}

\newcommand*{\expandme}{good}

\ExplSyntaxOn
\cs_new_protected:Npn \NewDocumentCommandExpandOnce
 {
  \exp_args:NNno \NewDocumentCommand
 }
\ExplSyntaxOff

\NewDocumentCommandExpandOnce \foo { s D<>{} } {%
  \expandme% Should get expanded once at definition time.
}

\renewcommand*{\expandme}{bad}

\begin{document}

\foo<bar>% Prints "bad", but I want it to print "good".

\end{document}

You can check that the output is “good”.

4
  • Thanks -- though I think this is an ancillary point, no? The core thing seems to be \exp_args_generate:n{VnV}.
    – Noldorin
    Sep 11 '21 at 22:53
  • @Noldorin not really: the final V form isn't used here, egreg's version uses a braced form that expands the content (which is \expandme` here) once and re-braces it. I used a final V argument where the final argument is necessarily a single token (\expandme in this case) also here (as in my first version) it just answers the question in the title (expanding the body) not simultaneously expanding the command name once (which could be done by replacing NNno here by NVno) Sep 11 '21 at 23:01
  • Yeah, though central to my issue was expanding the command name once at the same time. Admittedly, that's not in my title (would have made it too long I thought), though it seems to have been the root cause of my problem: that the \exp_args:NVnV variant does not exist by default. Anyway, this is a nice answer regardless, and supplements the other two. Now I don't know whether to give you or egreg the answer, since I decided on your (2nd) solution, but then it got amended to egreg's suggest (\exp_args_generate:n{VnV}) hah.
    – Noldorin
    Sep 11 '21 at 23:14
  • ... in any case, thanks to all three of you for your helpful answers.
    – Noldorin
    Sep 11 '21 at 23:14

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.