8

According to the interface3 documentation \tl_to_str:N expects a “tl var”, which I believe might be e.g. \g_temp_tl.

\tl_to_str:V should, if I got it right, expect an argument representing a control sequence referring to a variable, i.e. a token list variable, which I thought is something like \g_temp_tl.

So what is the difference?

Please give one example with \tl_to_str:N where the V-variant would be wrong and another example with \tl_to_str:V where the N-variant would be wrong!

  • Take a look at this example to understand the difference between N, n, and V: dpaste.com/3WRPSGA Basically V will perform one step of expansion whereas N takes the token unmodified. – Henri Menke Sep 7 at 23:22
  • @HenriMenke So what am I supposed to feed into each \tl_to_str:*? – Robert Siemer Sep 7 at 23:57
  • 1
    On documentation: as others have I think said, texdoc expl3 gives the general intro, interface3 is really intended as a formal reference. (I've written some introductory stuff, but probably we really do need to get 'Programming expl3' sorted!) – Joseph Wright Sep 8 at 7:29
9

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.

8

\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.

  • On the side: are you supposed to use \tl_new:N here? Is a \..._new:... never needed or just not with token lists, integers and floats? Officially? – Robert Siemer Sep 8 at 0:11
  • 1
    So in the spirit of my comment to Henri: What is proper use of \tl_to_str:N where the V-variant would be wrong and what is proper use of \tl_to_str:V where the N-variant would be wrong? – Robert Siemer Sep 8 at 0:45
  • 2
    @RobertSiemer The important difference is that :V variants unpack any kind of variable and pass the result as argument to the corresponding :n function. Token lists are a special case of this general variable unpacking, so they also work with :V. OTOH, an :N function works only with a single token and may have a different behavior than the corresponding :n function. Of course, for token lists the :N and :V functions will likely do the same thing. I guess the two functions are provided mainly for efficiency and consistency reasons. – siracusa Sep 8 at 2:34
  • 1
    @RobertSiemer On your question about \tl_new:N: I don't need to call it because I only used scratch variables that are defined by the kernel. (The int part would not work otherwise.) If I had used different variable names, I would have had to define them first using the \..._new:N functions. – schtandard Sep 8 at 6:30
  • 1
    @schtandard Yes, you are right: V-type can be used for any variable which has a single value (we perhaps should make fp 'print nicely', but it's a bit tricky). So tl, int, dim, skip, muskip, (fp) but not say prop or seq. Box registers are excluded as they don't have a meaningful 'value' here (at least without LuaTeX). – Joseph Wright Sep 8 at 6:53

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.