LaTeX3 - Expand nested argument

MWE:

\tl_set:Nn \l_tmpa_tl {a}
\tl_set:Nn \l_tmpb_tl {\mathbf{\l_tmpa_tl}}
\tl_set:Nn \l_tmpa_tl {b}
$$\l_tmpb_tl$$


The code snippet above actually produce $$\mathbf{b}$$. What I want to achieve is to produce $$\mathbf{a}$$, i.e., expand the l_tmpa_tl in the second line. However l_tmpa_tl is nested inside the \mathbf command and I do not know how to expand it. I tried

\exp_args:NNx \tl_set:Nn \l_tmpb_tl {\exp_args:Nx \mathbf{\l_tmpa_tl}}


but it doesn't work.

3 Answers

You need:

\tl_set:Nx \l_tmpb_tl { \exp_not:N \mathbf { \exp_not:V \l_tmpa_tl } }


Your approach doesn't work for two reasons. First, you can't nest x-type expansion, because x-expansion itself does not work inside x (or e) type expansion. Second, is that \exp_args:N... does not stop the function from expanding, so even after \tl_set:Nx \l_tmpb_tl { \exp_args:Ne \mathbf { ... } } (e-type expansion does work inside x- and e-type expansion), \mathbf would be expanded. What you need here is \exp_not:N to prevent expansion. Then, you can also replace \exp_args:NNx \tl_set:Nn by the simpler \tl_set:Nx, and as a matter of safety, prevent further expansion of \l_tmpa_tl by using \exp_not:V (\exp_not:V \l_tmpa_tl will expand the value of \l_tmpa_tl, and prevent further expansion).

In general case, just define auxiliary functions to do the work.

\tl_set:Nn \l_tmpa_tl {a}
\cs_set_protected:Npn \__tmp:n #1 {  % when this is executed, it will set \l_tmpb_tl to the desired result
\tl_set:Nn \l_tmpb_tl {\mathbf{#1}}
}
\cs_generate_variant:Nn \__tmp:n {V}
\__tmp:V \l_tmpa_tl


Alternatively, since the operation is expandable you can do this...

\cs_set:Npn \__tmp:n #1 {  % this expands to **exp_not** of the desired result
\exp_not:n {\mathbf{#1}}
}
\cs_generate_variant:Nn \__tmp:n {V}
\tl_set:Nx \l_tmpb_tl {\__tmp:V \l_tmpa_tl}


... which is usually more flexible/convenient.

• Yes, I agree that this is quite hard to figure out by yourself & read only interface3.pdf, I add this one to my idiom list post. Apr 10 at 17:36
• The exp_not thing is also a expl3 kind-of idiom... Apr 10 at 17:45

In addition to the other answers, in case you wish to get more familiar with expl3's various expansion-types:

If you want to rely on the statement of interface3.pdf that token-list-variables can be retrieved directly, i.e., without needing a \tl_use:N, within a single expansion-step, you can combine o-expansion with \exp_after:wN:

\ExplSyntaxOn
\tl_set:Nn \l_tmpa_tl {a}
\tl_set:No \l_tmpb_tl {\exp_after:wN \mathbf \exp_after:wN {\l_tmpa_tl}}
\tl_set:Nn \l_tmpa_tl {b}
%$$\l_tmpb_tl$$
\tl_show:N \l_tmpa_tl
\tl_show:N \l_tmpb_tl
%
% Neither \documenclass nor \begin{document} was used, so let's end the
% latex-run with the sledgehammer:
%
\stop


If you don't like \exp_after:wN, you can trigger f-expansion and have that stopped by an explicit space-token ~ or by an implicit space-token \exp_stop_f: which gets discarded by the f-expansion-mechanism when stopping:

\ExplSyntaxOn
\tl_set:Nn \l_tmpa_tl {a}
\tl_set:Nf \l_tmpb_tl {\exp_args:NnV \use:n {\exp_stop_f: \mathbf} \l_tmpa_tl}
\tl_set:Nn \l_tmpa_tl {b}
%$$\l_tmpb_tl$$
\tl_show:N \l_tmpa_tl
\tl_show:N \l_tmpb_tl
%
% Neither \documenclass nor \begin{document} was used, so let's end the
% latex-run with the sledgehammer:
%
\stop


If you want to make sure that things might still work out when expl3-policy reagarding direct usage of token-list-variables changes, you can combine o-expansion, \exp:w/\exp_end:-expansion and \exp_args:NnV:

\ExplSyntaxOn
\tl_set:Nn \l_tmpa_tl {a}
\tl_set:No \l_tmpb_tl {\exp:w \exp_args:NnV \use:n {\exp_end: \mathbf} \l_tmpa_tl}
\tl_set:Nn \l_tmpa_tl {b}
%$$\l_tmpb_tl$$
\tl_show:N \l_tmpa_tl
\tl_show:N \l_tmpb_tl
%
% Neither \documenclass nor \begin{document} was used, so let's end the
% latex-run with the sledgehammer:
%
\stop

• Small (not really important) nitpicks: a more idiomatic way to end an f expansion is \exp_stop_f: (an implicit space token) rather than ~. Also, N and V arguments don't need braces around them (doesn't hurt either) Apr 11 at 16:01
• @PhelypeOleinik Thanks for pointing out opportunities for improvement. I have just edited my answer to take these things to heed. :-) Apr 11 at 16:41