13

What is the \providecommand equivalent of \DeclareMathOperator?

I.e. as \providecommand is to \newcommand a method for defining new commands if and only if they have not already been defined?

Actually, as pointed out by @egreg in the comments, \providecommand can be dangerous since it might adversely affect the behaviour of packages that define it. What I would really like is something that combines the ability of \providecommand to quietly ensure a command exists with the functionality of \CheckCommand to make sure that the provided definition is consistent with previous definitions.

mforbes
  • 5,571
  • Of course there are lots of ways of doing this, \leting the previous definition to \relax, or using the equivalent of \providecommand{\X}{\mbox{X}} for example, but it struck me as odd that there is not something better... Am I missing something? – mforbes Jul 17 '12 at 20:13
  • 1
    Using \mbox won't automatically give you the correct spacing around the operator (\DeclareMathOperator does this properly). – Ian Thompson Jul 17 '12 at 20:20
  • 2
    I wouldn't rely on such a \ProvideMathOperator. Let's say you want to define a "fi" operator. If you say \ProvideMathOperator{\fi}{fi} and start using \fi, the resulting errors will be puzzling. I'm very cautious also with \providecommand. – egreg Jul 17 '12 at 20:43
  • @egreg I agree: What I am actually looking for is a command like \providecommand but that emits an error if the definition is not the same. I though I saw something like that before, but I can't recall where I saw it. – mforbes Jul 17 '12 at 21:36
  • Just a note: looking at the LaTeX3 interface in xparse, \DeclareBlah... defines without checking. Thus, the amsmath \DeclareMathOperator behaves as if it were called \NewMathOperator. – mforbes Jul 17 '12 at 21:40

2 Answers2

6

I'd be very cautions in using \providecommand or variations thereof. This simple example may show why: if you say

\providecommand{\box}{something}

and then start using \box in your document, very puzzling error messages will be raised.

One might think to define a \ProvideMathOperator that also checks for the definition of the first argument, if already defined somehow, and warns if the control sequence we are trying to define is an operator (with the same definition) or not.

However this is quite hard: the supplied control sequence might have any definition, with mandatory or optional arguments, be robust or even be unexpandable.

One could deal with all these variations, in principle, but it's rather hard (I assure you it really is). The number of predefined math operators is not very big, after all, so the "use \DeclareMathOperator and try with another name if LaTeX complains" way seems to be the less complicated one.


Here's a rather simplistic procedure:

\documentclass{article}
\usepackage{amsmath,xparse,l3regex}

\ExplSyntaxOn
\NewDocumentCommand{\ProvideMathOperator}{smm}
 {
  \cs_if_exist:NTF #2
   {
    \xopn_check:Nn #2 { #3 }
   }
   {
    \IfBooleanTF{#1}
      { \DeclareMathOperator*{#2}{#3} }
      { \DeclareMathOperator{#2}{#3} }
   }
 }
\cs_new_protected:Npn \xopn_check:Nn #1 #2
 {
  \cs_if_exist:cTF { \cs_to_str:N #1\c_space_tl }
   {
    \tl_set:Nx \l_xopn_name_tl { \token_get_replacement_spec:c { \cs_to_str:N #1 \c_space_tl } }
   }
   {
    \tl_set:Nx \l_xopn_name_tl { \token_get_replacement_spec:c { \cs_to_str:N #1 } }
   }
   \xopn_check_operator:Nn #1 { #2 }
 }
\cs_new_protected:Npn \xopn_check_operator:Nn #1 #2
 {
  \regex_match:nVTF { \A \\qopname\ \\(newmcodes@|relax)\ (o|m) \{ #2 \} \Z } \l_xopn_name_tl
    { \msg_warning:nnx { xopn } { samedefinition } { \token_to_str:N #1 } }
    { \msg_warning:nnxx { xopn } { differentdefinition } { \token_to_str:N #1 } { \l_xopn_name_tl } }
 }
\cs_generate_variant:Nn \regex_match:nnTF {nV}
\cs_generate_variant:Nn \token_get_replacement_spec:N {c}
\msg_new:nnn { xopn } { samedefinition }
 {
  The~operator~`#1'~already~exists~with~the~same~definition
 }
\msg_new:nnn { xopn } { differentdefinition }
 {
  The~command~`#1'~has~already~a~different~definition\\
  \\
  #2\\
  \\
  (If~the~above~reads~`\token_to_str:N \scan_stop:'~don't~
  redefine~`#1'~under~any~circumstances,~but~be~cautious~anyway)
 }
\ExplSyntaxOff

\ProvideMathOperator{\log}{log}
\ProvideMathOperator{\gcd}{gcd}
\DeclareMathOperator{\Tor}{Tor}
\ProvideMathOperator{\Tor}{Tor}

\ProvideMathOperator{\null}{null}
\ProvideMathOperator{\fi}{fi}

I just try and see whether the proposed control sequence exists or not; in the latter case \DeclareMathOperator is safe. In the former case, I look whether the "command with trailing space in the name" exists; then I compare the meaning of the command (using the one with the trailing space, if existent) to what amsmath would have defined it if it's an operator.

The output of the document is

*************************************************
* xopn warning: "samedefinition"
* 
* The operator `\log' already exists with the same definition
*************************************************
*************************************************
* xopn warning: "samedefinition"
* 
* The operator `\gcd' already exists with the same definition
*************************************************
*************************************************
* xopn warning: "samedefinition"
* 
* The operator `\Tor' already exists with the same definition
*************************************************
*************************************************
* xopn warning: "differentdefinition"
* 
* The command `\null' has already a different definition
* 
* \hbox {}
* 
* (If the above reads `\scan_stop:' don't redefine `\null' under any
* circumstances, but be cautious anyway)
*************************************************
*************************************************
* xopn warning: "differentdefinition"
* 
* The command `\fi' has already a different definition
* 
* \scan_stop: 
* 
* (If the above reads `\scan_stop:' don't redefine `\fi' under any
* circumstances, but be cautious anyway)
*************************************************

When the warning is issued, no definition is performed.

\ProvideMathOperator accepts the *-variant just like \DeclareMathOperator.

David Carlisle
  • 757,742
egreg
  • 1,121,712
5

I don't think such a command exists in amsmath, but you can create your own using \ifdefined.

\documentclass{article}
\usepackage{amsmath}
\newcommand\ProvideMathOperator[2]{\ifdefined#1\else\DeclareMathOperator{#1}{#2}\fi}
\DeclareMathOperator{\goat}{goat}
\ProvideMathOperator{\sheep}{sheep}
\ProvideMathOperator{\goat}{} %Has no effect because \goat is already defined.
\begin{document}
$\goat$ $\sheep$ 
\end{document}
Ian Thompson
  • 43,767
  • As I understand it, the \provide-command would define OR redefine something, based on its existence/not. That is, not just do nothing if it already exists. – Werner Jul 17 '12 at 20:24
  • ...see the definition of \provide@command in latex.ltx. – Werner Jul 17 '12 at 20:30
  • 1
    @Werner --- I think you are mistaken. I just did a simple test and \providecommand did not overwrite an existing definition. – Ian Thompson Jul 17 '12 at 20:35
  • 1
    @Werner \providecommand doesn't redefine an already existent command. – egreg Jul 17 '12 at 20:38
  • @egreg: True, and strange. – Werner Jul 17 '12 at 20:49
  • @Werner That's exactly what \providecommand is supposed to do. Let's say you need a command that's provided by a package that you may or may not load (perhaps in a simplified version). You put \providecommand in the preamble. But it's important to know what's the syntax of the "emulated" command, or chaos will ensue when you load that package. That's why I'm cautious with it. – egreg Jul 17 '12 at 20:59
  • @mforbes That's \CheckCommand. – egreg Jul 17 '12 at 21:35
  • @egreg: So \providecommand doesn't stand for Please Redefine Or Vaguely Idenfitify Da Existence of the COMMAND? :) In all seriousness, I understand the problems that may occur. – Werner Jul 17 '12 at 21:37
  • @egreg Thanks! I will modify the question to encourage that usage and discourage \providecommand. (P.S. I moved my question to the question comments... could you please move your answer?) – mforbes Jul 17 '12 at 21:43
  • @IanThompson: Would you mind if I update your answer to include a \CheckCommand to emphasize the dangers @egreg mentions? – mforbes Jul 17 '12 at 21:52
  • @mforbes --- be my guest. – Ian Thompson Jul 17 '12 at 21:55
  • @egreg: Having a little difficulty using \CheckCommand here since \DeclareMathOperator emits a robust command. See http://tex.stackexchange.com/questions/63670/how-to-use-checkcommand-with-robust-commands. – mforbes Jul 17 '12 at 22:35