In LaTeX3 programming, how does the V function parameter specifier differ from the o specifier? How is the o specifier meant to be used in contrast to the V specifier?

A related question: V vs. x


2 Answers 2


The o-specifier is 'lower-level' and does exactly one expansion of the argument: \foo:o {...} is equivalent to \expandafter \foo:n \expandafter {...}. In contrast, V-type expansion provides the value of a variable, which may be either a tl/clist (stored as a macro) or an int/dim/skip (stored as a register). To deal with the latter, V-type expansion inserts the required \the to yield the value. Thus o-type expansion is only equivalent to V-type where you know that the value to expand is a macro. One may use o-type expansion for cases where V-type does not apply, e.g.

\foo:n { \use_none:n <some tokens> }

Notice that the o-type takes a balanced text argument (zero or more tokens in braces), whereas the V-type takes a single token. This is quite different in semantics.

  • Also while o accepts arbitrarily many tokens one should highlight that V only works with single token input.
    – Skillmon
    Jan 1, 2023 at 10:09

o means “expand one level”.

V means “get the value of”.

While this might produce the same result for tl or clist variables, this isn't the case with other variables.

Let's try a toy problem, namely extracting the most significant digit from the integer stored in any int variable or given explicitly.


 {% \msd* takes a variable
     \aad_msd:V #2
     \aad_msd:n { #2 }

\cs_new:Nn \aad_msd:n
  \__aad_msd:w #1 \q_stop
\cs_generate_variant:Nn \aad_msd:n { V,o }

\cs_new:Npn \__aad_msd:w #1 #2 \q_stop { #1 }

\int_new:N \l_aad_test_a_int
\int_set:Nn \l_aad_test_a_int { 1234 }
\int_const:Nn \c_aad_test_b_int {4321}

\tl_set:Nx \l_tmpa_tl { \msd{4321} } \tl_show:N \l_tmpa_tl
\tl_set:Nx \l_tmpa_tl { \msd*{\l_aad_test_a_int} } \tl_show:N \l_tmpa_tl
\tl_set:Nx \l_tmpa_tl { \msd*{\c_aad_test_b_int} } \tl_show:N \l_tmpa_tl


If you try this, you get

> \l_tmpa_tl=4.
> \l_tmpa_tl=1.
> \l_tmpa_tl=4.

Now replace \aad_msd:V #2 with \aad_msd:o { #2 } and you'll get

> \l_tmpa_tl=4.
> \l_tmpa_tl=\l_aad_test_a_int .
> \l_tmpa_tl=\c_aad_test_b_int .

Do you see what happens? Integer variables aren't expandable, so :o does nothing to them. The same with all variables that correspond to registers: dim, skip, muskip.

You must log in to answer this question.

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