From a previous question, @egreg's states:

The argument types N and n are the most common. The former means that the argument must be an unbraced single token, the latter means that the argument must be a braced list of tokens.

This is more or less clear to me, but it looks like the L3 interface does not enforce this for user-defined functions, here's an MWE:


    \cs_new:Nn \myFunc:N {<<#1>>}
    \myFunc:N {test}


The code above compiles fine, even if I am passing a braced list of tokens to function myFunc:N which is apparently declared to strictly take an unbraced single token.

This makes a big part of the L3 programming layer, i.e. function definitions and argument specifications seem like a mere syntactic convention. sigh.

Why are the argument types not enforced for user-defined functions as one would expect?


Strangely, the compiler does throw an error when I omit the colon :, for example if I declare a function myFunc as opposedto myFunc:N. So that seems to be enforced...

  • 5
    Any type and argument checking slows down the compilation. Do you really have an use case why this should be needed? The error if you remove the colon is at definition time: without a colon \cs_new:Nn can't detect the argument specification, use \cs_new:Npn instead. Apr 23, 2022 at 10:23
  • Most other programming languages rarely leave things like type safety, variable scope, and function signatures to human convention. Those are too important and too much a structural part of the grammar. Conventions can be broken. Is it the intent of the LaTeX3 project that people start to follow these conventions? That would be very prone to error....
    – user32882
    Apr 23, 2022 at 10:31
  • 4
    @user32882 TeX is not a programming language and also doesn't have a context-free grammar. Apr 23, 2022 at 10:32
  • 3
    @user32882 Also expl3 cannot get around the fact that macros are untyped. It merely makes the conventions more explicit to the programmer to warn of common mistakes. Apr 23, 2022 at 10:37
  • 2
    @user32882 Macro languages can never be typed, since, in essence, they are "just" doing text substitution and outputting runnable code and/or data. Like with keyboard macros, programming function-key F4 to produce \end, and F3 to 'run' F4. With a single input stream, I can divide a part of it into 10 logical units, by convention, and then put all my code into the first (or 4th) unit (in the right order): result is the same. Convention allows understanding and provides process-building flexibility, like variables do in algebra.
    – Cicada
    Apr 24, 2022 at 7:46

2 Answers 2


The programming layer expl3 is still based on the TeX language and the lookup for arguments is as defined by TeX's rules.

These rules only distinguish between delimited and undelimited arguments. And what expl3 calls “functions” are actually regular TeX macros; variables, depending on their type, can be either macros or registers.

The basic \cs_new:Nn function for defining other functions only deals with undelimited arguments. It takes two arguments: one a single token, which should be a valid name for a function including the signature; the second argument should be a braced list of tokens.

The signature is used in order to supply the correct parameter text for the underlying macro to be defined, because a declaration such as

\cs_new:Nn \mymodule_foo:nn { #1 -- #2 }

eventually becomes

\def\mymodule_foo:nn #1#2{#1--#2}

(just like \newcommand does). It is possible to define functions without a signature in their name, but it's generally not recommended: these would be user level commands and it's better to use \NewDocumentCommand for them.

On the other hand, if you do

\cs_new:Nn \mymodule_foo:N { --#1-- }

and then call

\mymodule_foo:N {text}

TeX will follow its own rules and accept the code without complaining, notwithstanding that the coding is wrong from the expl3 point of view.

Why is it not enforced that arguments are specified in the proper way as regards to braced or nonbraced? Because this would be generally impossible and, in any case, it would introduce a heavy overhead in terms of speed and efficiency.

  • 1
    "It is possible to define functions without a signature in their name, but it's generally not recommended: these would be user level commands and it's better to use \NewDocumentCommand for them." Unfortunately, I tend to overlook things in documentations. Therefore, to be on the safe side, I would like to ask: What is the reason for recommending defining user level commands in terms of \NewDocumentCommand? \NewDocumentCommand provides protection. Are there drawbacks when using \cs_new_protected:Npn for defining user level macros? Apr 23, 2022 at 13:48
  • 2
    @UlrichDiez You can also define expandable ones, with \NewExpandableDocumentCommand.
    – egreg
    Apr 23, 2022 at 13:49
  • 1
    @egreg Yes. But expandable ones can also be defined with \cs_new:Npn. Do I overlook possible drawbacks when using \cs_new_protected:Npn/\cs_new:Npn instead of \NewDocumentCommand/\NewExpandableDocumentCommand in situations where \New(Expandable)DocumentCommand's sophisticated argument-types (e.g. v/+v) and argument-pre-processors are not needed? Apr 23, 2022 at 13:55
  • 4
    @UlrichDiez That's just a recommendation. which I tend to adhere to as much as I can. It better divides the programming into layers: user level commands, declaration of variables, public functions and private ones.
    – egreg
    Apr 23, 2022 at 13:56
  • 1
    @egreg So the drawback with using \cs_new_protected:Npn/\cs_new:Npn for defining user-level-commands is that it makes distinguishing different programming-layers more difficult. Thank you very much for the clarification. I hope I always remember to take this to heart in the future for the sake of writing clearer code. Apr 23, 2022 at 14:01

In TeX - exception: LuaTeX-engines - expandably looking at the next token for checking if it is an explicit category-1-character token denoting a multi-token-argument, e.g., a curly opening brace of category 1, is not possible. You'd need to use something like/based on \futurelet, which is an assignment, which implies that the macro in question could not be carried out in expansion-contexts like \write, \message, \csname..\endcsname, \edef, x/e/f/o-expansion etc.

Besides this:

How to "enforce", e.g., x/e/f/o-type-arguments?

Quote from Wikipedia: "In computability theory, the halting problem is the problem of determining, from a description of an arbitrary computer program and an input, whether the program will finish running, or continue to run forever. Alan Turing proved in 1936 that a general algorithm to solve the halting problem for all possible program-input pairs cannot exist."

Some tokens of an e-type-argument might form an expansion-based computer-program; some other tokens of the e-type-argument might come from arguments of user-level-macros that could be considered the input for the computer-program. So checking/"enforcing" an e-type-argument might involve coming up with a rather general routine for checking if expansion of the tokens forming the e-type-argument—probably a combination of expansion-based computer program and input for that expansion-based computer-program—terminates at all.

Maybe this is not exactly a case of the problem of coming up with a general algorithm for a Turing-machine to solve the halting problem for all possible program-input pairs, but this reminds me of it.

How to "enforce", e.g., w-type-arguments?

How to "enforce", e.g., T/F-type-arguments?

By the way - don't underestimate syntactic conventions—they can be a great help in debugging. ;-)

You can define (user-level) macros/control-sequences where specifying argument-signature is not needed—just use \cs_new:Npn or variants thereof. The p-type-argument denotes parameter text like with \def.

  • I'm looking at section 4.3.2 of interface3.pdf and nowhere does it state that the function name may or may not contain colons. Where is this documented?
    – user32882
    Apr 23, 2022 at 10:36
  • This has nothing to do with the halting problem. Not every circular dependency is a halting problem. Apr 23, 2022 at 10:38
  • I have no idea what the halting problem is, but OK.
    – user32882
    Apr 23, 2022 at 10:40
  • @user32882 Section "1 Naming functions and variables" says if a control-sequence denotes an expl3-function, then the name of that control-sequence consists of components whereof one is called "function name" and the other is called signature/argument-specification. These components are separated from each other by :. The name of the function is divided into into logical units via _. With expl3's \cs_new:Npn and variants thereof (interface3.pdf, section "3.2 Defining new functions using parameter text") ... Apr 23, 2022 at 13:28
  • @user32882 ... you can define both control-sequences which have _ and : in their names and denote expl3-functions and define control-sequences which don't have :/_ in their name and thus probably cannot be considered expl3-functions but can be considered whatsoever (user-level-)control-sequences defined in terms of means provided by expl3. I edited the last sentence of my answer accordingly. Apr 23, 2022 at 13:28

You must log in to answer this question.

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