When to use tl_to_str:V and when tl_to_str:N?aliasing vs variants — preferred practices?Difficulty saving formatting to a sequence to be called later in my codeA Couple of Tidbits about the LaTeX3 tl_case:??? FunctionLaTeX3: If next in input stream is command X or environment X, do Y else ZMisunderstanding from l3keys documentationWhy does the TeX scanner process tokens for register numbers and macro names differently?Equivalent for bgroup egroup in LaTeX3Is it possible to separate the declaration of a LaTeX3 variable of type control-sequence from the act of setting its value?Building regex from another one
What are the minimum element requirements for a star?
Why would prey creatures not hate predator creatures?
Why do we use the particular magnitude of a force to calculate work?
Grammar explanation for ~よし
Are there any dishes that can only be cooked with a microwave?
A new type of builder pattern
What was the motive for inventing Gröbner bases?
Is it a mistake to use a password that has previously been used (by anyone ever)?
Music theory class: the interval between (B flat and B sharp) and (E sharp and E flat)
Euclidean Distance Between Two Matrices
Generalize superdense encoding
How to tell my mentor to be careful when discussing my work in progress around people I do not trust?
How to explain to traditional people why they should upgrade their old Windows XP device?
N-Dimensional Cartesian Product
Is "montäglich" commonly used?
Unable to Style the checkboxes in Sitecore Forms
Is every conformal manifold equivalent to a flat one with cone singularities?
Steampunk book about a bounty hunter teen girl in London
Can a UK passport valid for two months travel to Germany post-brexit?
Bach Invention BMW 792 - Fingering Advice
Can the treble clef be used instead of the bass clef in piano music?
Prove that the equation has only one real root.
If prey gave predators the corpses of members which had naturally died, would predators be able to subsist without killing the prey?
Does toddler keep hands around private parts?
When to use tl_to_str:V and when tl_to_str:N?
aliasing vs variants — preferred practices?Difficulty saving formatting to a sequence to be called later in my codeA Couple of Tidbits about the LaTeX3 tl_case:??? FunctionLaTeX3: If next in input stream is command X or environment X, do Y else ZMisunderstanding from l3keys documentationWhy does the TeX scanner process tokens for register numbers and macro names differently?Equivalent for bgroup egroup in LaTeX3Is it possible to separate the declaration of a LaTeX3 variable of type control-sequence from the act of setting its value?Building regex from another one
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
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 usage 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.
expl3 latex3 token-lists
add a comment
|
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 usage 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.
expl3 latex3 token-lists
Take a look at this example to understand the difference betweenN
,n
, andV
: dpaste.com/3WRPSGA BasicallyV
will perform one step of expansion whereasN
takes the token unmodified.
– Henri Menke
Sep 7 at 23:22
@HenriMenke So what am I supposed to feed into eachtl_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 'Programmingexpl3
' sorted!)
– Joseph Wright♦
Sep 8 at 7:29
add a comment
|
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 usage 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.
expl3 latex3 token-lists
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 usage 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.
expl3 latex3 token-lists
expl3 latex3 token-lists
edited Oct 15 at 0:17
Robert Siemer
asked Sep 7 at 23:12
Robert SiemerRobert Siemer
7075 silver badges12 bronze badges
7075 silver badges12 bronze badges
Take a look at this example to understand the difference betweenN
,n
, andV
: dpaste.com/3WRPSGA BasicallyV
will perform one step of expansion whereasN
takes the token unmodified.
– Henri Menke
Sep 7 at 23:22
@HenriMenke So what am I supposed to feed into eachtl_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 'Programmingexpl3
' sorted!)
– Joseph Wright♦
Sep 8 at 7:29
add a comment
|
Take a look at this example to understand the difference betweenN
,n
, andV
: dpaste.com/3WRPSGA BasicallyV
will perform one step of expansion whereasN
takes the token unmodified.
– Henri Menke
Sep 7 at 23:22
@HenriMenke So what am I supposed to feed into eachtl_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 'Programmingexpl3
' sorted!)
– Joseph Wright♦
Sep 8 at 7:29
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
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
@HenriMenke So what am I supposed to feed into each
tl_to_str:*
?– Robert Siemer
Sep 7 at 23:57
1
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
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
add a comment
|
2 Answers
2
active
oldest
votes
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
!)
documentclassarticle
usepackage[T1]fontenc
usepackageexpl3
begindocument
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 1024200000000000000;
par
tl_to_str:V l_tmpa_fp % s__fp __fp_chk:w 1024200000000000000;
ExplSyntaxOff
enddocument
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 1024200000000000000;
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.
...I am supposed to throw some non-token list attl_to_str:V
? This is some kind of supported and intended use case? What use case would that be!?
– Robert Siemer
Sep 8 at 0:14
Your answer is really good at pointing out my confusion here: iftl_to_str:V
is supposed to eat any variable, then I would expect it to turn them into proper string representations: works great for integers (wheretl_to_str:N
fails), but it is buggy for floating point 42. (If it is not buggy, than what is?)
– Robert Siemer
Sep 8 at 0:19
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 abouttl_new:N
: I don't need to call it because I only used scratch variables that are defined by the kernel. (Theint
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 makefp
'print nicely', but it's a bit tricky). Sotl
,int
,dim
,skip
,muskip
, (fp
) but not sayprop
orseq
. Box registers are excluded as they don't have a meaningful 'value' here (at least without LuaTeX).
– Joseph Wright♦
Sep 8 at 6:53
|
show 4 more comments
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 showtl_to_str:N
which is in primitive terms exactly
detokenizeexpandafter#1
and thus as fast as this task can be achieved.
add a comment
|
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "85"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f507422%2fwhen-to-use-tl-to-strv-and-when-tl-to-strn%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
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
!)
documentclassarticle
usepackage[T1]fontenc
usepackageexpl3
begindocument
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 1024200000000000000;
par
tl_to_str:V l_tmpa_fp % s__fp __fp_chk:w 1024200000000000000;
ExplSyntaxOff
enddocument
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 1024200000000000000;
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.
...I am supposed to throw some non-token list attl_to_str:V
? This is some kind of supported and intended use case? What use case would that be!?
– Robert Siemer
Sep 8 at 0:14
Your answer is really good at pointing out my confusion here: iftl_to_str:V
is supposed to eat any variable, then I would expect it to turn them into proper string representations: works great for integers (wheretl_to_str:N
fails), but it is buggy for floating point 42. (If it is not buggy, than what is?)
– Robert Siemer
Sep 8 at 0:19
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 abouttl_new:N
: I don't need to call it because I only used scratch variables that are defined by the kernel. (Theint
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 makefp
'print nicely', but it's a bit tricky). Sotl
,int
,dim
,skip
,muskip
, (fp
) but not sayprop
orseq
. Box registers are excluded as they don't have a meaningful 'value' here (at least without LuaTeX).
– Joseph Wright♦
Sep 8 at 6:53
|
show 4 more comments
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
!)
documentclassarticle
usepackage[T1]fontenc
usepackageexpl3
begindocument
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 1024200000000000000;
par
tl_to_str:V l_tmpa_fp % s__fp __fp_chk:w 1024200000000000000;
ExplSyntaxOff
enddocument
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 1024200000000000000;
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.
...I am supposed to throw some non-token list attl_to_str:V
? This is some kind of supported and intended use case? What use case would that be!?
– Robert Siemer
Sep 8 at 0:14
Your answer is really good at pointing out my confusion here: iftl_to_str:V
is supposed to eat any variable, then I would expect it to turn them into proper string representations: works great for integers (wheretl_to_str:N
fails), but it is buggy for floating point 42. (If it is not buggy, than what is?)
– Robert Siemer
Sep 8 at 0:19
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 abouttl_new:N
: I don't need to call it because I only used scratch variables that are defined by the kernel. (Theint
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 makefp
'print nicely', but it's a bit tricky). Sotl
,int
,dim
,skip
,muskip
, (fp
) but not sayprop
orseq
. Box registers are excluded as they don't have a meaningful 'value' here (at least without LuaTeX).
– Joseph Wright♦
Sep 8 at 6:53
|
show 4 more comments
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
!)
documentclassarticle
usepackage[T1]fontenc
usepackageexpl3
begindocument
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 1024200000000000000;
par
tl_to_str:V l_tmpa_fp % s__fp __fp_chk:w 1024200000000000000;
ExplSyntaxOff
enddocument
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 1024200000000000000;
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.
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
!)
documentclassarticle
usepackage[T1]fontenc
usepackageexpl3
begindocument
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 1024200000000000000;
par
tl_to_str:V l_tmpa_fp % s__fp __fp_chk:w 1024200000000000000;
ExplSyntaxOff
enddocument
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 1024200000000000000;
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.
edited Sep 11 at 7:03
answered Sep 8 at 0:05
schtandardschtandard
6,9231 gold badge16 silver badges34 bronze badges
6,9231 gold badge16 silver badges34 bronze badges
...I am supposed to throw some non-token list attl_to_str:V
? This is some kind of supported and intended use case? What use case would that be!?
– Robert Siemer
Sep 8 at 0:14
Your answer is really good at pointing out my confusion here: iftl_to_str:V
is supposed to eat any variable, then I would expect it to turn them into proper string representations: works great for integers (wheretl_to_str:N
fails), but it is buggy for floating point 42. (If it is not buggy, than what is?)
– Robert Siemer
Sep 8 at 0:19
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 abouttl_new:N
: I don't need to call it because I only used scratch variables that are defined by the kernel. (Theint
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 makefp
'print nicely', but it's a bit tricky). Sotl
,int
,dim
,skip
,muskip
, (fp
) but not sayprop
orseq
. Box registers are excluded as they don't have a meaningful 'value' here (at least without LuaTeX).
– Joseph Wright♦
Sep 8 at 6:53
|
show 4 more comments
...I am supposed to throw some non-token list attl_to_str:V
? This is some kind of supported and intended use case? What use case would that be!?
– Robert Siemer
Sep 8 at 0:14
Your answer is really good at pointing out my confusion here: iftl_to_str:V
is supposed to eat any variable, then I would expect it to turn them into proper string representations: works great for integers (wheretl_to_str:N
fails), but it is buggy for floating point 42. (If it is not buggy, than what is?)
– Robert Siemer
Sep 8 at 0:19
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 abouttl_new:N
: I don't need to call it because I only used scratch variables that are defined by the kernel. (Theint
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 makefp
'print nicely', but it's a bit tricky). Sotl
,int
,dim
,skip
,muskip
, (fp
) but not sayprop
orseq
. Box registers are excluded as they don't have a meaningful 'value' here (at least without LuaTeX).
– Joseph Wright♦
Sep 8 at 6:53
...I am supposed to throw some non-token list at
tl_to_str:V
? This is some kind of supported and intended use case? What use case would that be!?– Robert Siemer
Sep 8 at 0:14
...I am supposed to throw some non-token list at
tl_to_str:V
? This is some kind of supported and intended use case? What use case would that be!?– Robert Siemer
Sep 8 at 0:14
Your answer is really good at pointing out my confusion here: if
tl_to_str:V
is supposed to eat any variable, then I would expect it to turn them into proper string representations: works great for integers (where tl_to_str:N
fails), but it is buggy for floating point 42. (If it is not buggy, than what is?)– Robert Siemer
Sep 8 at 0:19
Your answer is really good at pointing out my confusion here: if
tl_to_str:V
is supposed to eat any variable, then I would expect it to turn them into proper string representations: works great for integers (where tl_to_str:N
fails), but it is buggy for floating point 42. (If it is not buggy, than what is?)– Robert Siemer
Sep 8 at 0:19
2
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
@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
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
@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
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
@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
|
show 4 more comments
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 showtl_to_str:N
which is in primitive terms exactly
detokenizeexpandafter#1
and thus as fast as this task can be achieved.
add a comment
|
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 showtl_to_str:N
which is in primitive terms exactly
detokenizeexpandafter#1
and thus as fast as this task can be achieved.
add a comment
|
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 showtl_to_str:N
which is in primitive terms exactly
detokenizeexpandafter#1
and thus as fast as this task can be achieved.
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 showtl_to_str:N
which is in primitive terms exactly
detokenizeexpandafter#1
and thus as fast as this task can be achieved.
edited Sep 8 at 7:26
answered Sep 8 at 6:52
Joseph Wright♦Joseph Wright
215k24 gold badges582 silver badges912 bronze badges
215k24 gold badges582 silver badges912 bronze badges
add a comment
|
add a comment
|
Thanks for contributing an answer to TeX - LaTeX Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2ftex.stackexchange.com%2fquestions%2f507422%2fwhen-to-use-tl-to-strv-and-when-tl-to-strn%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Take a look at this example to understand the difference between
N
,n
, andV
: dpaste.com/3WRPSGA BasicallyV
will perform one step of expansion whereasN
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 'Programmingexpl3
' sorted!)– Joseph Wright♦
Sep 8 at 7:29