LaTeX3: Correct way to define a macro with :o

If I want to define a macro in LaTeX3 syntax with one-level expansion of its argument (:o), I figured that it is not enough to \cs_new it that way. Instead, I have to \cs_new it as :n and then create an :o variant.

Is this a bug or a feature? If the latter, it is not well documented.

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\cs_new_protected:Nn \__a_one:o
{
\tl_clear_new:N \__a_tmp_tl
\tl_set:Nn \__a_tmp_tl {#1}
\tl_show:N \__a_tmp_tl  % returns \__a_abc_tl - Why is there no expansion?
}
\cs_new_protected:Nn \__a_two:n
{
\tl_clear_new:N \__a_tmp_tl
\tl_set:Nn \__a_tmp_tl {#1}
\tl_show:N \__a_tmp_tl  % correctly returns abc with the :o variant
}
\cs_generate_variant:Nn \__a_two:n {o}
\tl_new:N \__a_abc_tl
\tl_set:Nn \__a_abc_tl {abc}
\__a_one:o { \__a_abc_tl }
\__a_two:o { \__a_abc_tl }
\end{document}

• Welcome to TeX.SX! You can have a look at our starter guide to familiarize yourself further with our format. – cfr Jan 4 '15 at 23:29
• The correct way (if I understood it right) is to define the macro with n type argument and then define a variant with \cs_generate_variant:Nn. The types o, V, x, v, or c all should be defined in terms of variants of n or N type arguments and never directly. – cgnieder Jan 4 '15 at 23:36
• You might be interested in Franks answer here: tex.stackexchange.com/questions/95114/… – cgnieder Jan 4 '15 at 23:38
• It's the way it works; with \cs_new:Npn and friends you can define only functions having n, N (or w) in their signature, because they are essentially \def (or similar) in disguise. Other argument types must be introduced with \cs_generate_variant:Nn – egreg Jan 5 '15 at 0:16
• @Manuel No, definitely not: the argument to the “external function" would qualify either as n or N. – egreg Jan 5 '15 at 0:23

Let me quote the final paragraph of section 3.1 in interface3.pdf:

Finally, the functions in Subsections 3.2 and 3.3 are primarily meant to define base functions only. Base functions can only have the following argument specifiers:
N and n No manipulation.
T and F Functionally equivalent to n (you are actually encouraged to use the family of \prg_new_conditional: functions described in Section 1).
p and w These are special cases.

The \cs_new: functions below (and friends) do not stop you from using other argument specifiers in your function names, but they do not handle expansion for you. You should define the base function and then use \cs_generate_variant:Nn to generate custom variants as described in Section 2.

Here's what section 2 says:

\cs_generate_variant:Nnparent control sequence{variant argument specifiers}

This function is used to define argument-specifier variants of the ⟨parent control sequence⟩ for LaTeX3 code-level macros. The ⟨parent control sequence⟩ is first separated into the ⟨base name⟩ and ⟨original argument specifier⟩. The comma-separated list of ⟨variant argument specifiers⟩ is then used to define variants of the ⟨original argument specifier⟩ where these are not already defined. For each ⟨variant⟩ given, a function is created which will expand its arguments as detailed and pass them to the ⟨parent control sequence⟩.

There's no magic involved. When you say \cs_new_protected:Nn, you're using \def or \gdef in disguise. While it could be possible, in principle, to examine the given signature and define the function with the suitable expansion mechanism, it's undoubtedly better to go step by step:

\cs_new_protected:Nn \__a_one:n
{
...
}
\cs_generate_variant:Nn \__a_one:n { o }


because the \__a_one:n function would have to be defined anyway. So there's no point in setting up a complicated mechanism for this.

To be more specific, after that code, the meaning of \__a_one:o would be

\exp_args:No \__a_one:n


There would be no other practical way for a hypothetical \cs_new_protected:Nn \__a_one:o handling the required expansion to basically define \__a_one:n under the hood and then applying \cs_generate_variant:Nn.

My experience says that this is good: you always know what functions you have available, because you define all of them.

• Thanks! Great answer! Because some argument types worked, I was under the impression, I could define all of them via \cs_new. As others might do the same mistake, I'd like to suggest two improvements. a) Have a one-liner, footnote or similar in sections 3.2 and 3.3 that refers to the important paragraph in 3.1 b) Have \cs_new create a warning for bad function names (e.g. including o in arguments) – for all guys like me who forget what they once read. – mh390 Jan 5 '15 at 8:24
• Option b) is not possible. While it is correct that \cs_new... doesn't handle expansion for you, there are rare cases where you want to handle the expansion yourself rather than through the outer expansion mechanism. – Frank Mittelbach Jan 6 '15 at 7:54