How to redefine or patch the '\newcommand' command?

The Problem


I'm surprised that I couldn't find more than vague hints towards this concept. It feels like such an obvious thing to do.

Motivation

A simple usecase, and the first package I plan write, is to ensure the following: that using \newcommand to define a command - say: \cmd - that has already been defined does not immediately generate an error. \cmd would simply be in a state of conflict. Subsequently trying to expand \cmd would then generate an error (since it's ambiguous which of the two definitions you want).

The conflict could be resolved by subsequently redefining \cmd using \renewcommand, after which \cmd can once again safely be expanded. Example:

\newcommand{\cmd}{FIRST}   %
\cmd                       % outputs FIRST
\newcommand{\cmd}{SECOND}  % no problem yet
\cmd                       % error: expanding ambiguous command
\renewcommand{\cmd}{THIRD} %
\cmd                       % outputs THIRD



Pseudo Code Solution

It feels like I have to do something like this (ignoring the optional argument for now):

\MetaRenewCommand{\newcommand}{
\ifdefined#1
\old@newcommand{#1}{
\PackageError{lazyfail}{Expanding ambiguous command \protect #1}
}
\else
\old@newcommand{#1}{#3}
\fi
}


Of course, there are many things wrong with this code. There is no \MetaRenewCommand and I still have to handle the optional argument of \newcommand.

So, how do I start? It feels like it must be possible, as \newcommand and friends are not primitives of LaTeX, but defined in terms of lower level commands.

Further Motivation

Here's another use-case I have in mind for this. When including a package, I would like to ignore all commands it provides except for a small list which I specify:

\usepackagefor[\Lightning]{marvosym}


Here I am loading the marvosym package, but only to use \Ligntning. I choose this example because the marvosym \CheckedBox command conflicts with the one in the llncs class.

I already routinely specify the commands I plan to use a package for with a comment, but it would be nice to actually enforce that.

-
\providecommand provides your "no problem yet" requirement... but not generating an error upon subsequent usage. –  Werner Sep 19 '12 at 21:35
It might be useful to (perhaps separately) ask about the wider thing you're trying to do. You mention 'many packages', so it would be handy to see if there is some better way to achieve the bigger goal than fiddling around with LaTeX kernel commands. –  Joseph Wright Sep 19 '12 at 21:38
Werner: Assume that I am the package writer, and have no control over the code in the 'Motivation' section. –  mhelvens Sep 19 '12 at 21:39
Joseph: Well, the 'Motivation' section already describes a standalone usecase, I think. I want to expand it, but that's only going to require more control over how the author's commands are defined. Also, this is a good opportunity for me to learn more about the guts of TeX. Would you humour me? :-) –  mhelvens Sep 19 '12 at 21:42


\documentclass{article}
\usepackage{letltxmacro,xparse}
\makeatletter
\DeclareDocumentCommand{\newcommand}{s+mo+o+m}{%
\begingroup
\edef\x{%
\endgroup
\ifdefined#2%
\unexpanded{%
\renewcommand{#2}%
{%
\PackageError{lazyfail}
{Expanding ambiguous command \protect #2}%
\@ehc
}%
}
\else
\noexpand\saved@newcommand
\IfBooleanT{#1}{*}%
{\noexpand#2}%
\IfNoValueF{#3}{[#3]}%
\IfNoValueF{#4}{[\unexpanded{#4}]}%
{\unexpanded{#5}}
\fi
}%
\x
}
\makeatother
\begin{document}
\newcommand{\cmd}{FIRST}   %
\cmd                       % outputs FIRST
\newcommand{\cmd}{SECOND}  % no problem yet
\cmd                       % error: expanding ambiguous command
\renewcommand{\cmd}{THIRD} %
\cmd                       % outputs THIRD
\end{document}


The approach here is to grab all of the arguments to the redefined \newcommand in one go, then work out whether they are to be 'recycled' or not.

Of course, you could set all of this up without xparse, but it would be a pain in the next: lots of \@ifstar and \@ifnextchar ( or slightly better \@testopt) stuff and several auxiliaries.

-
Nice! I can almost fully understand this one. And thanks for introducing me to xparse! Why doesn't everyone use it all the time? :-) –  mhelvens Sep 20 '12 at 9:52
@mhelvens xparse is quite new (at least in the usable form it's in now). –  Joseph Wright Sep 20 '12 at 10:19
I have one question about your code: Why do you need \begingroup and \endgroup there? Thanks! –  mhelvens Sep 20 '12 at 10:22
@mhelvens The group here means that \x is not affected outside of the use here to expand material. This is a standard 'trick' to avoid breaking anyone else's code. –  Joseph Wright Sep 20 '12 at 10:34
Ah, I see. That's even better than using @, which I thought was the standard approach for that. Thanks! –  mhelvens Sep 20 '12 at 11:16

Instead of \newcommand I would redefine \@ifdefinable. Then also some other things like \newcounter or \newsavebox are catched:

\documentclass{article}

\makeatletter
% Save old meaning of \@ifdefinable in \saved@ifdefinable
\newcommand*{\saved@ifdefinable}{}
\let\saved@ifdefinable\@ifdefinable
% Redefine \@ifdefinable
% #1: command token
% #2: code that defines the command in #1
\renewcommand{\@ifdefinable}[2]{%
% Here the same test for checking #1 is used as in the
% original definition.
\edef\reserved@a{\expandafter\@gobble\string#1}%
% \reserved@a contains the name without backslash
\@ifundefined\reserved@a{%
\saved@ifdefinable{#1}{#2}%
}{%
% Report the command with the name clash in the .log file
\@latex@info{Ambigous command: \string#1}%
% Redefine the command to generate an error message.
% \@ehd is the standard help text that starts with "You're in trouble here."
\def#1{\@latex@error{Expanding ambiguous command}\@ehd}%
}%
}
\makeatother

\begin{document}

\newcommand*{\cmd}[1]{FIRST(#1)}
\cmd{argument}

\newcommand*{\cmd}[2]{SECOND(#1,#2)}
\cmd{param1}{param2}

\renewcommand*{\cmd}{THIRD}
\cmd

\end{document}


"Border cases":

• LaTeX complains if someone tries to define a command \end... starting with end or in the case of \relax. These cases are not redefined by the above redefinitions.

• Treatment of arguments is ambiguous. In the case with the ambiguous error the arguments remain untouchted in the input.

• Commands are only detected, if they are defined via the LaTeX interface (\newcommand, \newcounter, \newsavebox, …). Definitions can also be done by TeX's primitive commands \def, \edef, \gdef, … or plain TeX commands (\newcount et. al.) These commands do not differentiate between new and old commands.

Of course, \newcommand could be redefined to drop some definitions and keep \Lightning as only command of package marvosym, but
• Other macros, defined without \newcommand, might be available with undesired effects.
• The kept macro might rely on other macros that are defined by \newcommand and not kept.
@mhelvens Further reading for plain TeX: TeX by Topic. LaTeX internals: source2e.pdf. –  Heiko Oberdiek Sep 19 '12 at 22:36