expl3 includes the o, f, and x argument specifiers for one-level and the two kinds of full expansion, respectively, before passing the argument to the base function. What is the preferred way of expanding an argument twice before passing it to the base function?

As an example, I'm trying to write a l3keys key-value interface to macros created with \DeclarePairedDelimiter from the mathtools package.

\tl_new:N \l__mymodule_size_tl
\keys_define:nn { mymodule }
    size .choices:nn =
      { big , Big , bigg , Bigg }
        \tl_set:Nn \l__mymodule_size_tl
          { [ \use:c { \tl_use:N \l_keys_choice_tl } ] }
      } ,

    size / auto .code:n = 
      \tl_set:Nn \l__mymodule_size_tl {*} ,

    size / none .code:n = 
      \tl_clear:N \l__mymodule_size_tl

Usually, the size of the delimiters is passed to the macro as an optional argument of \big etc. or *, such as \abs[\big]{x} where \DeclarePairedDelimiter\abs{\lvert}{\rvert}. I would prefer to have size = big etc. or size = auto as a key-value interface, such as \myabs[size=big]{x}.

The problem with the above code comes in correctly setting \l__mymodule_size_tl in the big etc. choices: I need to expand [ \use:c { \tl_use:N \l_keys_choice_tl } ] twice before assigning those tokens to \l__mymodule_size_tl.

  • If I use no expansion (i.e., \tl_set:Nn), the call to \abs gets its argument in the wrong form -- \l__mymodule_size_tl will hold [ \use:c { \tl_use:N \l_keys_choice_tl } ], not [\big].
  • If I use one-level expansion (i.e., \tl_set:No) and prepend [ after the expansion is complete, \l__mymodule_size_tl will hold [ \cs:w \tl_use:N \l_keys_choice_tl \cs_end: ], not [\big].
  • If I use either f or x expansion, \l__mymodule_size_tl will hold a bunch of "garbage", not [\big].

What can I do? Here is an idea: Use two o expansions, with some acrobatics between.

    \tl_set:No \l__mymodule_size_tl
      { \use:c { \tl_use:N \l_keys_choice_tl } ] }
    \exp_args:NNV \tl_set:No \l__mymodule_size_tl 
    \tl_put_left:Nn \l__mymodule_size_tl { [ }

This is rather ugly, in my opinion. Is there anything better that I can do?

  • 1
    Are you looking for \tl_set:Nx \l__mymodule_size_tl { [ \exp_not:c { \tl_use:N \l_keys_choice_tl } ] }?
    – egreg
    Apr 29, 2015 at 17:49
  • By the way, while it's not what this particular question asks for, future readers coming here because of the question title might be looking for \exp_args:No \function:o {...} which will pass double-o-expansion of {...} to \function. (also alluded to in Joseph Wright's answer below) -- although note that x-expansion (or the "true twice-single-expansion" of \expandafter\meaning\noexpand\empty is different from the result of the "twice-o-expansion" above.
    – user202729
    Jun 4, 2022 at 12:05

2 Answers 2


In days of yore there was a d-type argument for double expansion. However, it was dropped some years ago as there were very few places it was required, particularly when the team decided not to use toks registers generally. From memory, in expl3 there are two places where we had to go with a construct of the \exp_args:NNo \exp_args:No or similar form.

The reason that we did not keep d-type arguments is twofold. First, we want to avoid where possible relying on knowing the expansion detail of functions: this is required for low-level work but ideally should be relatively limited. The other reason is that it's very rarely needed as we have e-TeX available. The key to remember is that this allows us to control expansion in x-type contexts

\tl_set:Nx \l__mymodule_size_tl
  { [ \exp_not:c { \tl_use:N \l_keys_choice_tl } ] }

Whilst it is not needed here, we do document \cs:w ... \cs_end: for situations where exactly one expansion is required, so you could have

\tl_set:No \l__mymodule_size_tl
  { \exp_after:wN [ \cs:w \tl_use:N \l_keys_choice_tl \cs_end: ] }

though I would not use that here myself.

  • In an ideal world you would of course not be passing an argument here to a second document command (\abs) but to a code-level interface with a fixed number of mandatory arguments. Perhaps one day ...
    – Joseph Wright
    Apr 29, 2015 at 17:54

My impression is that you want to do

\tl_set:Nx \l__mymodule_size_tl
  [ \exp_not:c { \tl_use:N \l_keys_choice_tl } ]

With x expansion \exp_not:c is triggered, so the argument is fully expanded as always when c is involved (it is \csname...\endcsname); then the resulting token is made not expandable, so if you have


you'll get [\big] in your token list.

  • Thanks for the answer. Yes, \exp_not:c is exactly what I am looking for. Both your and Joseph's answers are great! But since I had to choose one to accept, I chose Joseph's since he covers double expansion. Apr 29, 2015 at 19:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .