In expl3, say we want to assign a value to a token list variable \l_foo_tl while expanding the value a certain number of times. For a single expansion the easiest way is

\tl_set:No \l_foo_tl { ... }

For more expansions we can use

\exp_args:NNo \tl_set:No \l_foo_tl { ... }
\exp_args:NNNo \exp_args:NNo \tl_set:No \l_foo_tl { ... }

and so on. This doesn't scale nicely, though. For four expansions we already have to use \exp_args_generate:n to generate proper expansion functions.

Does expl3 provide a generic, expandable function that, once tiggered by a single expansion step, expands its argument a given number of times exactly? If not, what is the most idiomatic way of doing such an expansion series in expl3?

| improve this question | | | | |
  • 1
    There isn't anything for an specific number of expansions, but most likely an f expansion does what you want here. The f expansion will expand the head of the token list (as will o) until the the head of the token list is an unexpandable token. – Phelype Oleinik May 27 '19 at 23:25
  • f expands too far in most of the cases. I'm debugging function definitions and actually want to get the results after a certain number of expansion steps. – siracusa May 27 '19 at 23:34

There is now!

The LaTeX3 people will flay alive me when they see this :|

Here's an s-type expansion (s stands for “blame siracusa” for this ;-) that, unlike other expansion flavours, takes an argument. The argument is the number of expansions of o-type expansions of the token list. You need then to define your own \exp_args:N... commands that do the expansion of the argument. For example, to expand the first argument four times you can define:

\cs_new:Npn \exp_args:Niv { \::s {4} \::: }

or to f-expand the first, and expand the second two times:

\cs_new:Npn \exp_args:Nft { \::f \::s {2} \::: }

or the contrary (first→twice, second→f):

\cs_new:Npn \exp_args:Ntf { \::s {2} \::f \::: }

or yet expand some argument an arbitrary amount of times:

\cs_new:Npn \exp_times:nNs #1 { \::s {#1} \::: }

and so on. Here's the code and some proofs-of-functionality. I define a macro \a which expands to \b, \b to \c, and so on until \f expands to g (to count expansions easily):

enter image description here


\cs_new:Npn \::s #1#2 \::: #3#4
    \if_int_compare:w #1 > 0 ~
      \exp_after:wN \exp_after:wN
      \exp_after:wN \__siracusa_exp_step_s:nnnn
      \exp_after:wN \__siracusa_exp_end_s:Nnnnn
    \exp_after:wN {#4} {#1} {#2} {#3}
\cs_new:Npn \__siracusa_exp_step_s:nnnn #1#2#3#4
    \exp_after:wN \::s \exp_after:wN
      { \int_value:w \__int_eval:w #2-1 \__int_eval_end: } {#3} \::: {#4} {#1}
\cs_new:Npn \__siracusa_exp_end_s:Nnnnn #1#2#3#4#5
  { \__exp_arg_next:nnn {#2} {#4} {#5} }

% Examples
\cs_new:Npn \exp_times:nNs #1 { \::s {#1} \::: }
\cs_new:Npn \exp_times:nNnfso #1 { \::n \::f \::s {#1} \::o \::: }
\cs_new:Npn \weird_command:nnnn #1 #2 #3 #4
  { \tl_to_str:n {#1|#2|#3|#4} }




\exp_times:nNs {0} \tl_to_str:n { \a }\par
\exp_times:nNs {1} \tl_to_str:n { \a }\par
\exp_times:nNs {2} \tl_to_str:n { \a }\par
\exp_times:nNs {3} \tl_to_str:n { \a }\par
\exp_times:nNs {4} \tl_to_str:n { \a }\par
\exp_times:nNs {5} \tl_to_str:n { \a }\par
\exp_times:nNs {6} \tl_to_str:n { \a }\par

\exp_times:nNnfso {0} \weird_command:nnnn{\a}{\b}{\c}{\d}\par
\exp_times:nNnfso {1} \weird_command:nnnn{\a}{\b}{\c}{\d}\par
\exp_times:nNnfso {2} \weird_command:nnnn{\a}{\b}{\c}{\d}\par
\exp_times:nNnfso {3} \weird_command:nnnn{\a}{\b}{\c}{\d}\par
\exp_times:nNnfso {4} \weird_command:nnnn{\a}{\b}{\c}{\d}\par

| improve this answer | | | | |
  • 2
    That's fantastic! I like how easily those argument types are composable by using the various \:: functions. So can I take it we'll see \::s in the next expl3 release, as you are going to secretly add it to the repo while the rest of the team is asleep? ;) – siracusa May 28 '19 at 2:08
  • 3
    @siracusa we never sleep – David Carlisle May 28 '19 at 8:08
  • @siracusa I doubt this would be useful for more than academic purposes. For everything else there is the f expansion. Of course, if you provide an use case, a feature request could be added. It would be fun, though, if I did that ;-) – Phelype Oleinik May 28 '19 at 9:47
  • 1
    @DavidCarlisle “The empire on which the sun never sets” ;-) – Phelype Oleinik May 28 '19 at 9:58

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.