20

Let \example be an xparse defined command, which takes one optional and one mandatory argument. The definition of \example cannot be modified (e.g. because it comes from a third-party package).

The auhtor of \example forgot to handle an empty optional argument, so I want to patch it for my own use. The TeX-way would be to \let the original definition of \example to something else and use this backup in the redefinition.

This is not possible with LaTeX macros, which take an optional argument (but Heiko made letltxmacro for that). Also, this is not possible for xparse defined macros, because xparse wraps the actual definition into some very smart argument parser construct.

I made the following not working example, where I naively used \cs_new_eq:NN, which makes TeX go into an infinite loop (because \example is now calling itself recursively in the xparse logic).

\documentclass{article}
\usepackage{xparse}
\begin{document}

\ExplSyntaxOn
\NewDocumentCommand \example { o m }
 {
  #1 ~ #2
 }
\ExplSyntaxOff

\example[First]{Second}

\example{Second}

\ExplSyntaxOn
% This is obviously wrong
\cs_new_eq:NN \orig_example:wn \example

\RenewDocumentCommand \example { o m }
 {
  \IfValueTF { #1 }
   { \orig_example:wn [ #1 ] { #2 } }
   { \orig_example:wn { #2 } }
 }
\ExplSyntaxOff

\example[First]{Second}

\example{Second}

\end{document}
  • 2
    Do not do this: all xparse-defined commands should be interfaces to documented code-level functions. Thus if you need a different interface the correct way is to define a new command from scratch. We are of course still working on getting everything working this way! – Joseph Wright Jan 2 '16 at 10:58
  • @JosephWright I have a counter example there: siunitx. The \SI macro for instance calls to \__siunitx_… functions. – Henri Menke Jan 2 '16 at 11:01
  • I wrote siunitx v2 before we'd got all of the xparse stuff revised (and indeed part of the point of writing siunitx in expl3 was to push development of the latter). I'm well-aware that it needs revising to have defined interfaces, and the development version (github.com/josephwright/siunitx/tree/master) is going that way. (It's hard work as there are many things I would probably not do, with hindsight, but can't break existing documents.) – Joseph Wright Jan 2 '16 at 11:02
  • (ctd.) Thus at present for siunitx you would have to model new interfaces using 'internal' code functions (regrettably). However, I assume the question is general and we are aiming at best-practice (which is _still developing). – Joseph Wright Jan 2 '16 at 11:05
  • @JosephWright Yes, I'm looking for best practices there. Perhaps this can also be seen as a feature request for \LetDocumentCommand in xparse, but I became aware of that when I wanted to overwrite \num from siunitx. – Henri Menke Jan 2 '16 at 11:07
17

In general the answer is do not do this. The idea behind xparse is the document level commands (\example) are there to define the syntax but should be implemented by use of documented code-level functions (so something like \module_command:nn here). As such, any new or altered definitions should not pass 'document level' syntax around at all but should be defined using xparse and the code-level functions the original uses

\NewDocumentCommand \example { o m }
  {
    \module_command:nn {#1} {#2} % Oops, doesn't cover \NoValue
  }
...
\RenewDocumentCommand \example { o m }
  {
    \IfNoValueTF {#1}
      { \module_command:n {#2} } % Presumably different
      { \module_command:nn {#1} {#2} }
  }

As such, the team quite deliberately don't provide \LetDocumentCommand or similar.


The best practice for expl3 and xparse use has developed over some time and as such some packages written by the team themselves still need revision to follow them. From my own code, notes2bib is probably the 'model' package in terms of showing the correct approach. On the other hand, at the time of writing the release version of siunitx needs revision to follow them: the code was largely written in parallel with expl3 and xparse development. There is development ongoing to create a third version of siunitx which will be implemented with this clear separation. (I hope to get this ready during 2016.)

If you need to create a new interface for an xparse-defined command and it is built on non-documented code functions, at present you will have to use those

\RenewDocumentCommand \example { o m }
  {
    % To be revised once documented interfaces are available
    \IfNoValueTF {#1}
      { \__module_command:n {#2} } % Presumably different
      { \__module_command:nn {#1} {#2} }
  }

Most authors making use of expl3 are aware of this aim, but you might wish to check with them that such developments are ongoing.

  • Is there any guidance in the L3 docs concerning naming the highest level internal commands called by a document level command with a given name? Aside from more typing, one thing I don't like about this 'split' (but probably just my ignorance) is that it makes code more opaque i.e. it is harder to find what a command is doing in the source because you have an extra step involved in tracing it. But if the highest level internal commands followed some kind of consistent naming linking them to the document level commands, this would be a lot clearer from a user's point of view. – cfr Jan 7 '16 at 22:23
  • 1
    Good luck getting people to actually follow this.... – cfr Jan 7 '16 at 22:24
  • What I don't understand is why I should separate these. It just seems to mean more work.... And I don't really know, either, what counts as too much mixing up and what doesn't since there has to be some mixing up. Or, at least, there seems to be. (But there's also stuff which there isn't expl3 syntax for, I think, and where should that be?) I think I just don't get the idea. Or the motivation. Or something. – cfr Jan 24 '16 at 23:51
12

Commands defined with \NewDocumentCommand behave in an indirect way; the situation is similar to the one addressed by letltxmacro when dealing with commands defined with \DeclareRobustCommand, which is easier to describe.

When you do

\DeclareRobustCommand{\foo}[1]{...#1...}

LaTeX actually defines two macros, in a way that's essentially

\edef\foo{\noexpand\protect\expandafter\noexpand\csname foo \endcsname}
\expandafter\newcommand\csname foo \endcsname[1]{...#1...}

Note the trailing space in the name of the macro that does the real job. So, if you naively do

\let\origfoo\foo
\DeclareRobustCommand{\foo}[1]{\origfoo{#1}?}

the call \foo{x} in normal text, where \protect is \relax, would give, in succession, (using to denote the trailing space in macro names having it)

\protect\foo•{x}          % first level expansion
\foo•{x}                  % \relax disappears
\origfoo{x}?              % replacement text of redefined \foo•
\protect\foo•{x}?         % first level expansion of \origfoo
\foo•{x}?                 % \relax disappears

Yikes! Infinite loop!

If you try

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand \example { o m }
 {
  #1 ~ #2
 }

\cs_show:N \example

the terminal output would be (reformatted for better reading)

> \example=\protected macro:->\int_zero:N \l__xparse_processor_int
    \tl_set:Nn \l__xparse_args_tl {\example code }
    \tl_set:Nn \l__xparse_fn_tl {\example }
    \__xparse_grab_D:w []{-NoValue-}
    \__xparse_grab_m_1:w \l__xparse_args_tl .

So when you do \let\origexample\example you just make it the same as this macro, but a subsequent

\RenewDocumentCommand{\example}{...}{...}

would change the meaning of \example and of \example•code (again with a space in the name) which is actually the macro doing the real work. The same kind of infinite loop would arise, because the newly defined \example•code macro will contain a call of \origexample that will eventually call \example•code. Infinite loop.

Building \LetDocumentCommand is not conceptually difficult, nor it is adding such commands to the ones managed by xpatch or regexpatch, since they follow a common pattern just like \DeclareRobustCommand.

However the situation is very different: the main reason is that the way macros defined with \NewDocumentCommand work is not cast in stone. It could change abruptly if the team decides so: the usage of “private” functions with __ in their name means exactly that no package developer should rely on this particular implementation.

The situation with \DeclareRobustCommand is different, because the LaTeX2e kernel publishes the interface (and indeed some packages exploiting this exist, not only letltxmacro and xpatch).

The reasons why \LetDocumentCommand will not be provided by xparse have been explained by Joseph Wright. You now have the tools for managing it, if you dare. I won't. ;-)

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.