11

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 
      \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 '15 at 17:49
8

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 '15 at 17:54
5

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

size=big

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. – Henry DeYoung Apr 29 '15 at 19:13

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.