6

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}
Sean Allred
  • 27,421
mh390
  • 63
  • 1
    Welcome to TeX.SX! You can have a look at our starter guide to familiarize yourself further with our format. – cfr Jan 04 '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 04 '15 at 23:36
  • You might be interested in Franks answer here: http://tex.stackexchange.com/questions/95114/aliasing-vs-variants-preferred-practices/95175#95175 – cgnieder Jan 04 '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 05 '15 at 00:16
  • You could also use \cs_new_protected:Npn \__a_one:o if, for instance, you use \tl_set:No \__a_tmp_tl {#1} inside of it. – Manuel Jan 05 '15 at 00:20
  • 1
    @Manuel No, definitely not: the argument to the “external function" would qualify either as n or N. – egreg Jan 05 '15 at 00:23
  • @egreg I don't know what you are referring to. I made a mistake, and I thought he was using \cs_…:Npn instead of \cs_…:Nn. In case is that, then it was a mistake of mine, in case it wasn't, I don't know what you mean. – Manuel Jan 05 '15 at 00:25
  • @Manuel You should *never* use a signature with o for a function defined with \cs_new:Npn or \cs_new:Nn and friends. – egreg Jan 05 '15 at 00:26
  • @egreg Why? \cs_new:Npn \foo:o { \tl_set:No \l_tmpa_tl } or something similar, I know it's not the best, but is it *that* bad? – Manuel Jan 05 '15 at 00:39
  • @Manuel The argument to \foo:o would be \l_tmpa_tl which instead is of type N. Period. – egreg Jan 05 '15 at 00:43
  • @egreg No, the argument wouldn't be \l_tmpa_tl because it's in the definition. In any case, I agree with you in “you always know what functions you have available, because you define all of them”. – Manuel Jan 05 '15 at 00:45

1 Answers1

6

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.

egreg
  • 1,121,712
  • 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 05 '15 at 08: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 06 '15 at 07:54