23

After hours of banging my brain against my skull, I narrowed down an issue I'm having to the fact that \makeatletter doesn't work inside \newcommand. Commands that I call after \makeatletter are registered as undefined, as if \makeatletter didn't do anything.

Why is this the case?

In the example below, I define two commands. Calling one of them from the document body causes the undefined error, while the other doesn't.

\documentclass[11pt]{book}
\usepackage{kvsetkeys}

\newcommand{\commandThatFails}{
    \makeatletter \comma@parse{}{} \makeatother
}

\makeatletter
    \newcommand{\commandThatWorks}{
        \comma@parse{}{}
    }
\makeatother

\begin{document}

Hi there.

\commandThatFails
\commandThatWorks

\end{document}
  • 4
    Because you can't change the catcode of @ inside the body of the document. The second version is the correct one since it allows you to include control sequences with @ character and doesn't cause any problem upon execution. The first one is trying to change the catcode upon execution and failing. – percusse Sep 16 '14 at 0:06
  • @percusse I'm not sure that's the correct explanation… I mean, it partially is, but it's a bit of a mess. – Manuel Sep 16 '14 at 0:07
  • 1
    @Manuel I'm always up for a good schooling :) – percusse Sep 16 '14 at 0:08
  • 6
    this must be a duplicate, but it's the same reason you can't use \verb in a command argument. catcodes change the tokenisation of characters they do not affect tokens tat have already been parsed. when the argument to \newcommand is scanned you get token \makeatletter token \comma token @ token p token a token r token s token e when the command is executed the catcode of @ is changed but that has no affect on tokens – David Carlisle Sep 16 '14 at 0:14
  • 2
    @percusse you can change the catcode at any point in the document or in the preamble, but catcodes don't affect tokens. – David Carlisle Sep 16 '14 at 0:16
32

When \makeatletter is executed, it changes the catcode of @ to so it can be part of a macro name. However, when defining a macro or using an argument of a macro, the tokens are stored without being executed.

So here,

\newcommand{\commandThatFails}{
    \makeatletter \comma@parse{}{} \makeatother
}

\makeatletter is stored as a token in the command rather than being executed. Because @ is still a non-letter, the command parses as the tokens \comma, @, p, a, r, s, and e, which is not \comma@parse

However, in your second example, \makeatletter is executed before the command is defined, so that \comma@parse{}{} is parsed correctly:

\makeatletter
    \newcommand{\commandThatWorks}{
        \comma@parse{}{}
    }
\makeatother

This is for the same reason that \verb|...| fails within arguments: it’s not executed, just stored, causing any strange characters to cause all sorts of errors.

As noted by @percusse, in this cases where you need @-macros just once or twice, you can use \csname comma@parse\endcsname which expands all the macros and then converts every token between \csname and \endcsname to a macro name. So this would also work:

\newcommand{\commandThatDoesntFail}{\csname comma@parse\endcsname{}{}}

Where the command resolves to \comma@parse{}{} (note that the tailing {}{} is treated as arguments to \comma@parse, not \endcsname).

Further reading:

  • 13
    +1 (I've written answers with much worse English than that, and I am English:-) – David Carlisle Sep 16 '14 at 0:35

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.