User level commands are comparatively much fewer than internal functions in the LaTeX kernel.
Just to make an example, \parbox is defined in terms of
\@iparbox \@iiparbox \@iiiparbox \@parboxto
the last one being redefined each time \parbox is called. Each of the first three internal functions has a different parameter text and it's quite difficult to read the code.
Perhaps the example is misleading, but expl3 was born to get rid of such difficulties by first providing a layer for “normalizing” arguments and then a clearer interface for internal functions.
In expl3 style we'd have
\NewDocumentCommand{\parbox}{O{c}oomm}
{
\innerlevel_parbox:nnnnn {...}
}
that passes the arguments to an internal function with five arguments, as clearly specified by the function's signature.
The signature is what comes after the colon that should always appear in the name of an expl3 function.
For instance, \innerlevel_parbox:nnnnn would be defined with
\cs_new_protected:Nn \innerlevel_parbox:nnnnn { ... }
where ... stands for code using the five arguments. An alternative way would be
\cs_new_protected:Npn \outerlevel_parbox:nnnnn #1#2#3#4#5 { ... }
but I find that the former way is simpler and doesn't require code bits that can be deduced from the function's signature. On the other hand, the latter version is slightly more efficient.
You see that already \cs_new_protected:Nn has a signature itself, namely Nn. This tells the programmer that it should be followed by a single token (a control sequence, in this case) and then an argument in braces, neither of which is manipulated in any way.
After the definition, the programmer knows that \innerlevel_parbox:nnnnn should be followed by five arguments in braces.
Naming guidelines for the kernel functions also help in remembering what arguments a function will expect. For instance,
\hbox_set:Nn
expects a box variable as its first argument (a single unbraced token) and some text as the second argument. A more complex example is
\int_compare:nNnTF
but with some practice in expl3 programming one should be able to remember that the first argument should be an “integer denotation”, the second argument a comparator among <, = or > (single unbraced token), the third denotation another “integer denotation”, then two braced arguments for the code when the test is succesful or not, respectively.
You mention “manipulation” and this requires some explanation. One of the strong point of expl3 is the possibility of defining variants of functions.
With \cs_new(_protected):Nn we can only define functions whose signature only consists of N or n. But as soon as the function is defined, we can define variants thereof.
An example: \hbox_set:Nn is mostly similar to the user level function \sbox. But suppose you want to define a command that uses text stored in some macro, but using the current contents, to feed \sbox with. The legacy programming style would require
\expandafter\sbox\expandafter\mybox\expandafter{\container}
The analog with expl3 would look like
\hbox_set:NV \l_fluffy_my_box \l_fluffy_container_tl
after having done (just once, you do this at the outer level)
\cs_generate_variant:Nn \hbox_set:Nn { NV }
Thus the signature doesn't only tells the programmer how many arguments are expected: it also allows to modify how the function manipulates the arguments before the main function is called.
I dare you to write cleanly the code corresponding to \innerlevel_parbox:nnnnV. You can't do it with a bunch of \expandafter', but you'd need auxiliary function doing argument swapping in order to bring the fifth argument at the head, so \expandafter can be effectively used. Instead, the expl3 kernel already has the infrastructure to cleanly perform the task, without the programmer needing to know how the magic happens.
\foo:Nnwould be used as\foo:Nn \something {some tokens}– David Carlisle Oct 01 '23 at 19:13\cs_new:Nn \function_name:nWhilst theNmeans an argument, it is an argument to\cs_new, rather than an argument to\function_name. – Veak Oct 01 '23 at 19:49\cs_new:Nnnot\cs_new(etc.) – Joseph Wright Oct 01 '23 at 19:50\cs_newwith Argument Specifier:Nnappended ? – Veak Oct 01 '23 at 19:56\cs_new:Nnis a single token, as is\feuds_newteorema_simple:nn– David Carlisle Oct 01 '23 at 20:10\feuds_newteorema_simplewith the Argument Specifier being:nnto signify two arguments are to be passed to\feuds_newteorema_simple. Rather, you state that the names are\cs_new:Nnand\function_name:nmeaning that:is an acceptable character like_in function names.Latexin turn reads the function name from whichLatexextracts theArgument Specifier. Correct ? – Veak Oct 01 '23 at 20:15\cs_new:Nntells you the usage is\cs_new:Nn\foo{xxx}with one cstoken and one brace group, whereas the variant\cs_new:cnhas usage\cs_new:cn{foo}{xxx}wherecmeans it takes a token list of characters and makes the csname before passing to the underlying command. – David Carlisle Oct 01 '23 at 20:27\cs_new:Nnand\cs_new:Npn. Nevertheless, it was decided to just include apto tell you it uses parameter text, rather than invent a completely different name. – Veak Oct 01 '23 at 20:45\<module>_<description>:<args>, all three parts together built a single function name, e.g.,\tl_trim_spaces:n. The<module>part is just name-spacing, as TeX has no built in concept of name spaces (in our exampletlis the module), the<description>should briefly tell what the function should do (trim_spaces), and then the<args>tell us which arguments the function takes and of what kind (our example takes a singlen-type argument, so any number of tokens surrounded by braces). – Skillmon Oct 01 '23 at 22:14\cs_new:Nnand\cs_new:Npnexamples are of modulecs, they create a new one (new), and the first one takes a single token, and then a normal argument of any number of tokens in braces. The second one however takes a single token, then parameter text, and then any number of tokens in braces. Their behaviour is fundamentally the same (create a new something) as the description tells us, but they take different arguments to reach that goal (here a different number of arguments). – Skillmon Oct 01 '23 at 22:17