9

The file expl3.pdf - "The expl3 package and LaTeX3 programming" explains how expl3 functions should be named, according to the scheme
\⟨module⟩_⟨description⟩:⟨arg-spec⟩
or according to the scheme
\__⟨module⟩_⟨description⟩:⟨arg-spec⟩.

This raises two questions:

  1. How to precisely distinguish the term "function" from the term "macro"?

  2. Suppose it is about an interface for programmers, where a top-level macro is to be called by the programmer/user of the interface, which itself does not process any arguments, but which in turn initiates, among other things, an expansion cascade in the course of which internal macros are called that process the arguments.

    How should one handle the matter of argument specifications in such a scenario?

    I see two possibilities:

    • The argument specification of the toplevel macro is empty, i.e., the name of the toplevel macro ends with a colon :. The name of each internal macro comes with an argument specification that specifies exactly those arguments that are processed by that respective internal macro.

    • The argument specification of the top-level macro is not empty but specifies which types of arguments are processed by the macro-based mechanism as a whole in the course of the expansion cascade. The name of each internal macro comes with an argument specification that specifies exactly those arguments that are processed by that respective internal macro.
      This possibility raises the additional questions of

      • whether applying \cs_generate_variant:Nn to the top-level macro works out for such mechanisms and
      • how, for example, to deal with the argument specifications of top-level macros for tail-recursive macro mechanisms that process a variable number of arguments.

2 Answers 2

9
  1. How to precisely distinguish the term "function" from the term "macro"?

Eh... :)

There is no clear distinction really. I tend to write “function” when documenting user-level macros, and I use “macro” more often when writing code documentation. The term “function” in expl3 is used to distinguish macros-that-do-stuff (functions) from macros-that-hold-stuff (tl variables, for example), and “macro” is used less often, so in principle you should follow that.

  1. Suppose it is about an interface for programmers, where a top-level macro is to be called by the programmer/user of the interface, which itself does not process any arguments, but which in turn initiates, among other things, an expansion cascade in the course of which internal macros are called that process the arguments.

From the two options you mentioned, the latter. Even if a function doesn't directly (as in: after its first expansion) take an argument, its name must contain what arguments it will grab further down the line. This is precisely what makes \cs_generate_variant:Nn work in these cases. Taking a simple example from the kernel:

\cs_new_protected:Npn \clist_concat:NNN
  { \__clist_concat:NNNN \__kernel_tl_set:Nx }

\clist_concat:NNN doesn't take any argument, yet it's :NNN because some auxiliary will then take these three arguments. This is important because:

  1. (and most important) you don't have to dig up the internals of \clist_concat:NNN to figure out how many arguments it takes, because it's clear from its name; and
  2. \cs_generate_variant:Nn \clist_concat:NNN { ccc } will work because \cs_generate_variant:Nn knows how many arguments the function takes (and can throw an error if you try to make a :ccccc variant from that (then the generated variant will be simply \exp_args:Nccc \clist_concat:NNN).
  • how, for example, to deal with the argument specifications of top-level macros for tail-recursive macro mechanisms that process a variable number of arguments.

:w. If you write a recursive macro function like:

\cs_new:Npn \my_tmp:w #1
  {
    \if_meaning:w \scan_stop: #1
    \else:
      \my_do_stuff_with:n {#1}
      \exp_after:wN \my_tmp:w
    \fi:
  }

then use

\my_tmp:w { some } { arguments } { then } { end } { with } { \relax }

you don't know how many arguments the function will take because it depends on the input. This means that you cannot do \cs_generate_variant:Nn, because you have no way to know how many to process. Maybe, if the documentation of such \my_tmp:w said that it requires at least one actual argument, then you could call it \my_tmp:nw, and generate a variant for the first argument only.

2
  • So we have another distinction between "function" and "macro": With the term "macro" the focus is on toplevel-expansion: After toplevel-expansion the macro-token in question is gone/replaced. A function, however, can be formed by several macros, one toplevel-macro and probably some internal macros, i.e., is a mechanism based on a set of macros that form an expansion-cascade which in the end (besides side-effects like performing "scratch-assignments") delivers a set of tokens that forms the desired result.... Apr 7 at 12:38
  • ...With the term "function" the focus is on the result of the expansion-cascade initiated by calling the function's toplevel-macro/user-level-macro. Obtaining the result of a function may require several expansion steps. A macro-token is gone/replaced after a single expansion-step. Apr 7 at 12:38
5

Let me rephrease Phelype's answer on point 2 in some simple words:

The signature of an expl3 function (i.e., what comes after the colon in the name) is supposed to represent all arguments and the preprocessing of such arguments, in other words everything needed by that function to come after it. It is totally irrelevant if those arguments are picked up directly or indirectly (because of efficiency reasons or ...).

E.g., if you write \foo:nnn then everybody reading your code immediately knows that "foo" wants 3 braced argument after it.

The focus here is on: "look at the signature then you know what that function is picking up as arguments (and how, if the signature does some preprocessing with the arguments, e.g. uses "c" or "V" or ...)


This also explains our use of "function" versus "macros" (even though with TeX being a macro expansion language you can't fully hide from that fact (other by convention)). Functions are things for which you know by the signatures what arguments they take (except in the few cases where they are "weird" and have a :w signature) while macros are, well macros: the dirty stuff that places all kind of tricks on you or you place all kind of tricks with, which we try to avoid doing with expl3 programming (or at least confine to very internal stuff).

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.