When to use \tl_to_str:V and when \tl_to_str:N?

I see that schtandard has written an excellent answer, but thought I might elaborate a bit on the difference between n-, N- and V-type arguments. I'll also include a little history and TeXnical detail which might help illustrate a few ideas.

The core argument types for expl3 are N (a single token without braces) and n (zero or more tokens in a brace pair). For token lists, we use these two types to separate out functions that act on a token list variable from those that act on a 'raw' token list

\tl_map_function:NN \l_tmpa_tl \my_func:n
\tl_map_function:nN { abc } \my_func:n

Historically (up to around the time I joined the LaTeX team), we called token list variables 'token list pointers' (tlp), and the functions were separate: that made some ideas a bit clearer, but made others a lot more opaque and really was overall confusing.

Token list variables are ultimately TeX macros, so we can get their 'value' at a TeX primitive level using \expandafter, or in expl3 terms we could use \exp_args:No:

\exp_args:No \tl_map_function:nN \l_tmpa_tl \my_func:n

is thus equivalent to

\tl_map_function:NN \l_tmpa_tl \my_func:n

The problem there is I'm relying on the implementation of \l_tmpa_tl. We could have used TeX token registers (toks) for data storage, and they can only be converted to their content by the \the primitive. Thus applying \exp_args:No is problematic: ideally, using expl3 you don't need to know the implementation detail. (Provided the APIs remain unchanged, the team reserve the right to alter implementation.)

The V-type argument deals with this issue. It uses some clever code by Morten to 'look at' the variable and decide if it's a macro or a TeX register. It then does either \expandafter or \the as required, leaving just the 'value'. That means (as illustrated in the answer by schtandard) that we can use V-type expansion and apply it to tl, dim, etc. and not have to worry how they are implemented.

(Historically, expl3 had a separate module for toks, and these were used in parallel with tlp/tl: it was really important to have a simple way to get values. We altered tl data storage to avoid needing toks too, so this aspect doesn't show quite as strongly now.)

Almost all \tl_<thing>:N 'use' functions could replaced by \tl_<thing>:V, but there would be a performance hit. At the implementation level, the team are 'allowed' to take advantage of how things are defined in raw TeX terms. So for example \tl_to_str:N currently does use the fact that the tl can yield a value on a single expansion:

> \tl_to_str:N=\long macro:
#1->\__kernel_tl_to_str:w \exp_after:wN {#1}.
l.3 \show\tl_to_str:N

which is in primitive terms exactly

\detokenize\expandafter{#1}

and thus as fast as this task can be achieved.


\tl_to_str:V is a variant of \tl_to_str:n, i.e. it retrieves the value of any variable you throw at it and passes it to \tl_to_str:n. \tl_to_str:N on the other hand expects a token list variable and converts its value to a string. Importantly, this is a separate function from \tl_to_str:n, not a variant.

As long as you only pass token list variables to these functions, they behave identically, except for that \tl_to_str:N is marginally faster than \tl_to_str:V. If you pass different variable types to both functions, the result may differ, depending on the variable type. (It is never correct to pass anything but a token list variable to \tl_to_str:N!)

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage{expl3}

\begin{document}

\ExplSyntaxOn

\tl_set:Nn  \l_tmpa_tl  {42}
\int_set:Nn \l_tmpa_int {42}
\fp_set:Nn  \l_tmpa_fp  {42}

\tl_to_str:N \l_tmpa_tl  % 42
\par
\tl_to_str:V \l_tmpa_tl  % 42
\par
\tl_to_str:N \l_tmpa_int % \l_tmpa_int
\par
\tl_to_str:V \l_tmpa_int % 42
\par
\tl_to_str:N \l_tmpa_fp  % \s__fp \__fp_chk:w 10{2}{4200}{0000}{0000}{0000};
\par
\tl_to_str:V \l_tmpa_fp  % \s__fp \__fp_chk:w 10{2}{4200}{0000}{0000}{0000};

\ExplSyntaxOff

\end{document}

Note that the value retrieved by \exp_args:NV and passed to \tl_to_str:n when you call \tl_to_str:V is not necessarily in a human readable form (what you call a "string representation" in the comments). Let us instead call it a value representation. The defining property of this representation is that it is understood by the functions handling the respective variable type, i.e. you can input \s__fp \__fp_chk:w 10{2}{4200}{0000}{0000}{0000}; into any fp function instead of 42 and it would behave the same. (You can think of this like \exp_args:NV retrieving the binary representation of the variable value, though that is obviously just a metaphor.)

Formatting the value of a variable for human readers is an entirely different task. Note that, although it might look like \tl_to_str:N (or :V does this for token lists, this is not really true (in the sense that you get all the information determining the exact value of the variable), since it does not show category codes.


Regarding your request for examples: \tl_to_str:V \l_tmpa_int is right, \tl_to_str:N \l_tmpa_int is wrong. \tl_to_str:V is never wrong when you can use \tl_to_str:N (namely on tl variables), but it is slower.

The real difference between the two functions in the latter case is a conceptual one: \tl_to_str:N acts on the variable, \tl_to_str:V acts on its content. In this case, the result is the same: [convert the content of [this token list variable]] is the same as [convert [the content of this token list variable]]. With other functions, there may be differences, e.g. the :N version often assigns a new value to the variable while the :V version leaves the result in the input stream. There may also be differences in expandability.