LaTeX3 Interface for functions

I recently posted a question about writing a macro in plain TeX that would modify the elements in a user-specified list. I offhandedly mentioned my dislike for TeX's interface, so somebody turned me onto LaTeX3, which is supposed to be, as the manual promises, more like a modern programming language. I'm just starting out programming in TeX, so it's been an uphill battle trying to understand the manual. I assume that's because the manual was written for experienced TeX users; however, there doesn't seem to be an alternative out there for people just starting out in LaTeX3/TeX, so I've got no choice but to work with what I have. That's why I'm posting this. The manual is confusing, and I'd like to clear up some of that confusion by asking you some simple questions about syntax.

I should mention, the person who told me about LaTeX3, also gave me a solution to my original post using its interface. I was able to use this solution in conjunction with the manual to begin to work out some basic facts about the LaTeX3 syntax. I'm going to be talking about what I've managed to figure out, but fair warning, some of it's based on my own inferences - drawn with the help of the example provided by the stackexchange user - not explicit instructions in the manual, so expect mistakes. I'd like you to know, I'm not obfuscating matters when at times I use my own terminology. It's just hard to talk about a subject you don't fully understand in a structured way.

Also I'm writing this as a separate post, not a comment, because of it's length. Thanks in advance.

-----------------------------------------------------------------------------------------------------------------------

Function definitions.

What I've managed to figure out so far:

A new function is defined, among other ways, with the following code:

\cs_new_<restrictions>:Npn <function name> <function parameters> {<replacement code>}



The \cs_new_<restrictions> is a LaTeX command, the Npn is there to tell the interface's "parser" what it should expect after the \cs_new_<restrictions>: Npn part of the code, in this case, a single token control word, i.e. <function name>, one or more parameters, i.e. <function parameters>, and a token list, i.e. {<code>}, which replaces the function.

So if I want to define a new function that takes, say, 4 arguments I could go about it with the following code

\cs_new_<restrictions>:Npn \myfunction #1 #2 #3 #4 {<code>}


And similarly the code for a function with 2 arguments might look something like this

\cs_new_<restrictions>:Npn \myfunction #1 #2 {<code>}


Of course, I'm assuming - and correct me if I'm wrong - the spaces aren't necessary, cause the parser has already been told how to delineate the "meta" arguments (<function name>,<parameters>,{<code>}) from one another with the help of the "meta signature" Npn.

Now, if I want to do away with the #'s I can use the following generic command

\cs_new_<restrictions>:Nn <function name>:<function signature> {<code>}



Similar deal, except now the parser expects a <function signature> like Nn, NnN, TnN or something or other, after <function name>.

So again, a function with 4 arguments could look like this

\cs_new_<restrictions>:Nn \myfunction:NNNN {<code>}



and one with 2 arguments like this

\cs_new_<restrictions>:Nn \myfunction:NN {<code>}



There are other commands in the l3basics library for creating functions, but their general structure seems to be essentially the same. The only difference is in their functionality. So for example, using \cs_set... instead of \cs_new... makes the function local as opposed to global. I'll probably write a follow up post asking for more details on what e-type and x-type expansions are, but for now I think its best to stick to the big picture.

Anyways, is that right so far?

Okay, moving on.

Variable definitions.

What I've managed to figure out so far:

So there a quite a few data types in LaTeX3, but the main ones are token lists, strings, integers, sequences, and comma separated lists. They each use their own abbreviations, but in general, when defining a new variable, you declare the type and follow it up with a keyword like new or const depending on whether you're initializing the variable.

So for example, if I want to declare, but not initialize, a token list variable I use the code:

\tl_new:N \mytokenList


and then somewhere down the line, I can store a token list in \mytokenList with the code:

\tl_set:Nn \mytokenList {<tokens>}


But, if I know what data I want to store in the variable from the get go I can instead use this command (does not apply to sequences or integers)

\tl_const:Nn \mytokenList {<tokens>}


Aside: I noticed that even variables have "function signatures". Probably makes defining a parsing regime easier.

That's about as general as I can be, before I have to specify what data type I'm refering to, cause each has its own associated operations.

-----------------------------------------------------------------------------------------------------------------------

That's what I have so far. I'd appreciate any feedback. This stuff isn't easy to learn on your own! Especially with minimal knowledge of TeX, so I apologize if some of you're looking at this and thinking "well obviously". Anyways, thanks again.

• it would be helpful if you said what documentation you had found (texdoc interface3 being the main one) the answers might be different depending on whether or not they are starting from there. Sep 3, 2020 at 6:36
• @DavidCarlisle Hi, David. The manual I read can be found here ctan.mirror.rafal.ca/macros/latex/contrib/l3kernel/… Sep 3, 2020 at 7:15
• yes that's the one. It will be installed also on your system and available via the texdoc command. Sep 3, 2020 at 7:16
• try also texdoc expl3 (or texdoc.net/texmf-dist/doc/latex/l3kernel/expl3.pdf) for a higher level introduction and overview Sep 3, 2020 at 7:17
• @Harry It doesn't prevent as a compiled language would do: in TeX you can override basically anything: \def\def{def is not def anymore}. Instead, when you define a constant variable in expl3 you tell who's reading the code (including you) that the value should not change (and if you load \usepackage[check-declarations]{expl3}, you will get an error if you try, say, \tl_set:Nn \c_my_tmp_tl { wrong }). As for declaration+initialisation: no, there's no expl3 function for that. Those two steps are deliberately separate (but I too sometimes feel lazy enough to define one :-) Sep 3, 2020 at 23:37

There are two main ways to define functions:

\cs_new<restrictions>:Npn

\cs_new<restrictions>:Nn


where can be _protected, _nopar or _protected_nopar.

Both ways check that the N-argument (that is, a single token) following is a control sequence (or active character) that is currently undefined and globally define the control sequence.

What's the difference? That the first family requires, after the control sequence to be defined, a “parameter text” before the { delimiting the “replacement text” of the function.

The “parameter text” can be any sequence of tokens including #1, #2 and so on up to #9. However, in order to appreciate the full power of this freedom, you need to be acquainted with chapter 20 of the TeXbook and the concept of “delimited argument”.

Let's keep it simple, though. The two following pieces of code are completely equivalent:

\cs_new:Npn \harry_foo:nn #1 #2 { -#1-#2- }
\cs_new:Nn \harry_foo:nn { -#1-#2- }


because the latter will automatically supply the parameter text #1#2 based on the signature of the function to be defined, in this case :nn.

The signature should consist of a (possibly empty) sequence of n and N characters.

Note that spaces are ignored when \ExplSyntaxOn is active, so

\cs_new:Npn \harry_foo:nn #1 #2 { -#1-#2- }
\cs_new:Npn \harry_foo:nn #1#2 { -#1-#2- }
\cs_new:Npn \harry_foo:nn #1#2{ -#1-#2- }


are all equivalent. There could be a space even after #, but I'd not recommend it.

The syntax rules of TeX specify that when it is expecting a “parameter text” (basically, when doing \def or similar assignments and after having stored the name of the macro to be defined) everything up to the first { is part of the parameter text. There is no way to foresee what the parameter text is, hence the special p argument specifier that just means “everything up to {”.

Only simple parameter text such as #1, #1#2 and so on can be automatically generated, which is done when one uses the second family \cs_new<restrictions>:Nn.

Where are you wrong? In assuming you can use T as a specifier in the signature. The argument specifiers T or F are added when \prg_new_conditional<restrictions>:Nnn is performed.

Also your analysis of the parameter text is wrong, as shown before.

What about \cs_set<restrictions>:Npn and :Nn? Everything as above applies, with the difference that the function to be defined is not checked about being defined or not and its meaning will be silently overwritten, but the scope of the declaration coincides with the current group. Typically, \cs_set... is used for temporary functions that need to adapt to the context so their meaning is not fixed.

The naming conventions for variables recommends that their name starts with l, g or c. Actually, variables used in expl3 code should conform to the convention; it's possible to use “normal” names such as \myTokenList for variables of type tl (maybe also clist) that are to be used in the document.

Variables starting with l should always be acted upon locally (\tl_set:Nn, say), while variables starting with g should always be acted upon globally (\tl_gset:Nn, say).

Variables starting with c are constants and should be never acted upon after having been assigned a value, but only used.

One can define constants with

\tl_const:Nn \c_harry_foo_tl {<tokens>}
\str_const:Nn \c_harry_foo_str {<tokens>}
\clist_const:Nn \c_harry_foo_clist {<comma list>}
\seq_const_from_clist:Nn \c_harry_foo_seq {<comma list>}
\prop_const_from_keyval:Nn \c_harry_foo_prop {<key-value list>}
\int_const:Nn \c_harry_foo_int {<integer expression>}
\fp_const:Nn \c_harry_foo_int {<fp expression>}
\bool_const:Nn \c_harry_foo_bool {<boolean expression>}
\dim_const:Nn \c_harry_foo_dim {<dimen expression>}
\skip_const:Nn \c_harry_foo_dim {<skip expression>}
\muskip_const:Nn \c_harry_foo_dim {<muskip expression>}
\intarray_const_from_clist:Nn \c_harry_foo_intarray {<comma list>}
\regex_const:Nn \c_harry_foo_regex {<regex>}
\cc_tab_const:Nn \c_harry_foo_cctab {<code>}

• Thanks Egreg. Couple of questions. So <function name> should always include a function signature, regardless of whether one uses Npn or Nn after the first colon? If it's disregarded when an explicitly parameter list is provided what's the point of including it, to improve readability when someone else looks at my code? Also, you said \tl_set:Nn is a variable that starts with an l. Are you referring to the one in \tl, cause that also appears in \tl_gset:Nn. Also I thought \tl was an abbreviation for token list. Sep 3, 2020 at 18:58
• @Harry Unless you're defining a user level function, but in LaTeX an interface with xparse is preferable. A local variable name should start with l independent of what kind of variable it is. \tl_set:Nn is a function, whose first argument should be a local tl variable, say \l_harry_foo_tl. Sep 3, 2020 at 19:40
• Okay, that makes sense. So include a function signature no matter what when using the two commands you mentioned in your answer, use proper naming conventions when declaring variables, and use xparse when defining user-level functions. Did I get that right? Sep 4, 2020 at 6:13
• @Harry That’s correct. Sep 4, 2020 at 9:00