14

This is somewhat related to a question I asked earlier here.

I'm trying to split a token using a delimiter. However, the delimiter is stored in a token. Looking at the most recent documentation, I have the following options:

 \seq_set_split:Nnn
 \seq_set_split:NnV

and the corresponding global variants.

Now suppose that I have the following defined,

\tl_new:N \l__delimiter_tl
\tl_set:Nn \l__delimiter_tl {;}

assume further that I have

\tl_new:N \l__content_string_tl
\tl_set:Nn \l__content_string_tl {a;b;c;d}

I would like to be able to write something like the following:

\seq_new:N \l__possible_values_seq
\seq_set_split:NnV \l__possible_values_seq {\l__delimiter_tl} \l__content_string_tl

But that doesn't work.

I've also tried doing something like

\seq_new:N \l__possible_values_seq
\seq_set_split:NnV \l__possible_values_seq {\tl_use:N \l__delimiter_tl} \l__content_string_tl

But that doesn't work either.

Now I know I can create a variant such as

\cs_generate_variant:Nn \seq_set_split:Nnn {NVV}

because I could then write

\seq_set_split:NVV \l__possible_values_seq \l__delimiter_tl \l__content_string_tl

which will get the job done. However, in the link above, someone commented that it would be bad form to write such a variant. I'd like to keep to the spirit of that comment.

So my question, how do I interpolate a token-list before it's read. It seems that there should be some way of doing this. In TeX, I would do something like

\expandafter\<macro_name> {\<first_argument>}....

However, I'm still trying to understand the syntax of LaTeX3, it seems that part of what's making LaTeX3 nice is you can avoid the near unreadability of code like

\exp_after:wN \exp_after:wN \exp_after \seq_set_split:NnV \exp_after:wN \l__possible_values_seq {\l__delimiter_tl} \l__content_string_tl

which, incidentally, I tried and I got garbage out.

So, how do I get \l__delimiter_tl to get expanded before \seq_set_split:NnV tries to read it?

EDIT

In the comments to my earlier post, there was some discussion of creating aliases for LaTeX3 functions. For example, writing something like

\cs_new_eq:NN \seq_length:N \seq_count:N

But, in the comments to that post, it was remarked that this would be a bad naming convention. Am I misunderstanding the difference between aliases and variants?

EDIT II

It seems that instead of creating a variant I could write:

\exp_args:NNVV \seq_set_split:Nnn \l__possible_values_seq \l__delimiter_tl \l__content_string_tl

and avoid using \cs_generate_variant:Nn.

I get that \cs_generate_variant:Nn might be preferred. But it seems that there might be circumstance---such as a one time occurrence for the need of the variant in a package---where generating the variant might be less clear. Any thoughts on this point?

13
  • 1
    I can't find where it's not recommended to do \cs_generate_variant:Nn \seq_set_split:Nnn { NVV }. I'd say that it's the good method.
    – egreg
    Commented Jan 24, 2013 at 0:01
  • @egreg. Even so, is there a way to accomplish what I want without having to take recourse to \cs_generate_variant:Nn?
    – A.Ellett
    Commented Jan 24, 2013 at 0:03
  • One of the main purposes of the LaTeX3 kernel is to provide functions for frequent tasks. If the right function is there, use it! \cs_generate_variant:Nn is the recommended method for doing such variations. You might do \exp_args:NV \seq_set_split:NnV \l__delimiter_tl \l__content_string_tl, which is exactly what the built variant would do.
    – egreg
    Commented Jan 24, 2013 at 0:04
  • Look at the whole \exp_args:... commands. But @egreg is right: variants are a much cleaner way (and they simply use \exp_args:... internally (I think))
    – cgnieder
    Commented Jan 24, 2013 at 0:06
  • For the record, I fully agree with egreg (and I doubt you'll find anyone on the LaTeX team telling you not to define variants when needed). Maybe you are looking for \exp_args:NNV \seq_set_split:NnV which is equivalent to \seq_set_split:NVV (once defined), but not advisable. Commented Jan 24, 2013 at 0:09

2 Answers 2

29

As I think the concept of "variant" in expl3 is one of the most fundamental concepts for LaTeX3 I like to expand a bit on the answer already given by Joseph.

Naming convention

The naming convention for commands in LaTeX3 (expl3) structures the command name into

\module_description:arg-specifiers

  • module identifies the (main) type of data the function manipulates or use (for example, int (integers), prop (property lists), etc., or it might be the name of a package or some specific concept
  • description says what is being done, e.g., put_left, get, clear, count, etc. If it makes sense the same descriptions are reused, but for special tasks there can, of course, be some that are used only once.
  • arg-specifiers finally describe what arguments the function has and how they should be treated (more on this below).

Base functions

The base arg-specifiers are:

  • N denoting that the argument consists of a single token (no braces needed, sometimes not even allowed)
  • n denoting a "normal" argument with braces
  • p denoting a parameter spec like #1#2 in definitions
  • T is like n but tells the programmer that this is the "true" path in a conditional
  • F is like n but signals the "false" path in a conditional

Commands with arg-specifiers consisting just of those letters are called base functions.

Variant functions

There are many more arg-specifiers available, for example

  • c interpret the argument as a string and generate a command name from it
  • V the argument is a variable, use its value
  • v the argument is a string, generate a variable name from it and then use the value of that variable
  • o expand the argument once like in \expandafter{...}
  • x expand the argument as if it would have been inside the replacement text of \edef
  • f expand the argument fully until you hit a non-expandable token then stop expanding

Any command that uses one or more of these arg-specifiers is called a variant of the corresponding base function. The important aspect here is that all these additional arg-specifiers modify the argument in one way or another and only then pass it to the underlying base function. For example:

   \foo_bar:cVno {cmd} \VAR {text} \CMD

would

  • generate from the string cmd the command name \cmd
  • look up the value of the variable \VAR
  • leave text alone
  • expand \CMD once and surround the result with braces

and only then it would call the corresponding base function as follows:

   \foo_bar:Nnnn \cmd {<value-of-\VAR>} {text} {<one-level-expansion-of-\CMD>}

Now ideally we want any possible variant of a base function automatically available for a programmer. Unfortunately, this can only be reliably done if alle variants have all been predefined (as TeX doesn't offer you to trap the "undefined csname" error and do something on the fly).

Given the number of arg-specifier and the possible permutations predefining all variants, of which 90% would never be needed, is not realistic. We therefore adopted the following strategy:

  • conceptually all variants are available and everybody can assume this is the case
  • in reality the kernel only defines a small subset that is often needed
  • any variant not defined by the kernel needs to be defined by the programmer using \cs_generate_variant:Nn
  • \cs_generate_variant:Nn has been designed in such a way that it doesn't matter if it is called several times: if the variant already exists it will do nothing. So if two programmers define the same variant in their packages it doesn't hurt, the first one executed will define the variant the second one will simply be ignored (with very little overhead).
  • If some variants are used fairly often they may eventually get defined already in the kernel. Because of the last point it doesn't hurt if some packages still define the variant, i.e., there is no need for programmers to modify their packages in that case.

So in summary: Whenever you need a variant that is not predefined, define it at the beginning of your code. This is even sensible if you need the variant only once, because the code using the variant will be much more readable than any manual preprocessing of the argument and the speed difference is close to zero.

The \exp_args:N... functions

Technicically speaking a variant defined via \cs_generate_variant:Nn has a very simple definition: \foo_bar:cVno above would simply expand to

\exp_args:NcVno \foo_bar:Nnnnn

So instead of defining the variant and then using it like this:

\cs_generate_variant:Nn \foo_bar:Nnnnn {cVno}

   ...
   \foo_bar:cVno {cmd} \VAR {text} \CMD

one could, of course, directly use its replacement text, i.e.,

   \exp_args:NcVno \foo_bar:Nnnn {cmd} \VAR {text} \CMD

If you do that, then you optimise for speed (you avoid one expansion - hurrah) at the cost of less readable code and except in very very special circumstances this is not useful. In the kernel there are a few commands that have been very carefully optimised for speed, but unless you expect your command to be called many thousand times in a document there is no way to even measure the speed gain. Concerning the question of clarity, even if a variant is used only once in the code it will make the code clearer as all the arg-specifiers have universal meanings I can immediately see what happens with the arguments regardless of what the base function does (which hopefully is clear from its module and description part as far as possible).

So on the whole the advice is: do not use the \exp_args:... in your code unless there is a very good reason. On "good" reason could, of course, be that you quickly want to test something and then throwing in some \exp_args... is quicker than defining variants and use them. But this is a little like the argument that one doesn't need to document this piece of code because it is only temporary there (... for the next 10 years).

For the same reason avoid \exp_after:wN! If you use it, you normally do something wrong in the sense of thinking in plain TeX/LaTeX2e expansion based programming style. (So why has the kernel code so much \exp_after:wN ??? well, there we provide the low-level machinery, and somewhere certain things need to happen :-) but this is not the model on which LaTeX3 programming is build upon.)

Other arg-specifiers

There are a number of other arg-specifiers that I didn't mention above. They are a bit strange and do not support variant generation in the above sense.

  • D means don't use this command. It is only for the kernel (if at all). This doesn't really tell if and how many args this command has.
  • w means this commands has a weird argument structure which is normally not further detailed. For example \exp_after:wN (known in plain TeX as \expandafter) is a little strange as it expands the second token (N) but the first can be a single opening or closing brace.

Aliases viz variants

In contrast to a variant (that manipulates the arguments before passing the result to the base function) is an alias of a command not providing any new functionality. It is simply a "copy" of a command under a new name. The reason why this can be useful is simply abstraction in the code (hiding implementation details) and as a side effect also readability.

For example, the data type seq (sequences) is internally implemented as simple token lists (tl) with some internal structure. Therefore to "clear" a sequence, i.e., \seq_clear:N is doing exactly the same as clearing a token list \tl_clear:N, i.e., the latter would (at the moment) clear the sequence as well. But providing a separate name here is nevertheless much better:

  • it makes the code more readable (you see that you are clearing a sequence)
  • the programmer doesn't need to remember which commands a specific to sequences and which not
  • there is a proper abstraction for the sequence type: if the implementation would get changed, code would not be affected. For example property lists changed the implementation from toks registers to tls.

So in summary an alias is something you would only want to use if you have a reason to provide existing fuctionality under a different name.

3
  • Thanks! This is really nice. One thing that tripped me up, and so might be worth emphasizing, is that that if you want to define a variant form of a function like \foo_bar:cVno then use \cs_generate_variant:Nn \foo_bar:Nnnnn {cVno}.have to define the base function with \cs_new:Npn \foo_bar:nnnn #1#2#3#4 {...} and then use \cs_generate_variant:Nn \foo_bar:Nnnnn {cVno}. I initially thought that I could define variants like \foo_bar:cVno directly but then found that this did not work.
    – user30471
    Commented Mar 27, 2018 at 7:35
  • @user30471 You forgot an N \cs_new:Npn \foo_bar:Nnnnn #1#2#3#4#5 {}, then it works \show\foo_bar:cVnon reads \exp_args:NcVno \foo_bar:Nnnnn. Commented Jan 8 at 20:01
  • Actually base functions are those functions which arg specifier starts with possibly many N or n. This head is the variable part, for example in \foo:nTn, only the heading n can change with \cs_generate_variant:Nn, not the trailing one. In order to define \foo:nTV, you can define it by hand: \cs_new:Npn \foo:nTV { \exp_args:NnnV \foo:nTn }. Here T behaves like n. Commented Jan 8 at 20:12
12

A variant is the correct way to solve your problem

\seq_set_split:NVV \l__possible_values_seq \l__delimiter_tl \l__content_string_tl

will expand the two token lists \l__delimiter_tl and \l__content_string_tl to their value (what \tl_use:N <name> would give) before executing the \seq_split:Nnn 'base function'. As the LaTeX3 kernel code does not generate all variants of every command, you do need

\cs_generate_variant:Nn \seq_set_split:Nnn { NVV }

here. That is safe even if this is later added to the kernel: the design of \cs_generate_variant:Nn means that it will not overwrite variants that already exist.

The creation of equivalent functions, such as

\cs_new_eq:NN \seq_length:N \seq_count:N

is a separate process: both of these do exactly the same thing, but have different names. In this case, you may need to temporarily do this as there has been some renaming within the kernel code. (expl3 is 'broadly stable', so we give notice of changes, but it's at the stage yet where nothing will be renamed or improved on.)

You must log in to answer this question.

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