[Tex/LaTex] loading tocloft makes \ifdefined\chapter TRUE in article class

chapterssectioningtocloft

I am using the article class, and I discovered that the tocloft package defines \chapter, which will mess up with my other stuff. How does this happen? Can I make \chapter "undefined" again after loading tocloft? Here is an MWE:

\documentclass{article}
% loading tocloft will make \chapter defined
\usepackage{tocloft}

\begin{document}

\ifdefined\chapter
   chapter is defined in the current \texttt{article} document.

   \chapter{This is not a chapter, but will be typeset as plain text}
\else
   chapter is \emph{not} defined.
\fi

\end{document} 

Best Answer

Some packages and macros check whether \chapter is defined or not in order to take some action; for example, to typeset the bibliography as a chapter (book and report classes) or as a section (article class).

The test they perform is based on \@ifundefined, something like

\@ifundefined{chapter}
  {\section*{\refname}}
  {\chapter*{\bibname}}

It's important to know that \@ifundefined returns true when the command given in the argument by its name is undefined or equivalent to \relax. Actually, whenever one uses

\@ifundefined{foo}{<true>}{<false>}

the command \foo will become equivalent to \relax if it weren't defined before.

Conversely, the \ifdefined conditional returns true also when the token after it is equivalent to \relax and this is why you observe this behavior: indeed tocloft checks for the existence of \chapter with \@ifundefined.

The reason for this is historical: LaTeX needs to know whether \foo is defined or not when \newenvironment{foo} is issued, or \c@foo for \newcounter{foo}. So the macro \@ifundefined takes as argument the name, rather than the token itself. It uses internally \csname...\endcsname which has the effect outlined before: if \foo is undefined, then using \csname foo\encsname will let it to be equivalent to \relax.

There should be no problem as long as you stick to \@ifundefined to test for definedness.

The \ifdefined and \ifcsname primitives are e-TeX extensions, so they were not available when LaTeX2e was released. There was really no other reliable way to check for definedness; a different strategy would have been to compare with an undefined token (there is \@undefined for this). However, the \csname...\endcsname problem would remain, unless one uses a slick trick:

\def\@ifundefined#1{%
    \begingroup\expandafter\expandafter\expandafter\endgroup
    \expandafter\ifx\csname#1\endcsname\@undefined
      \expandafter\@firstoftwo
    \else
      \expandafter\@secondoftwo
    \fi}

so that \@ifundefined{foo} wouldn't leave \foo equivalent to \relax if it wasn't defined. However this test is not completely expandable because of \begingroup and \endgroup, so the developers decided to leave \@ifundefined as it was.

The trick works because \csname foo\endcsname is performed before TeX scans the \endgroup and so the equivalence with \relax vanishes as soon as TeX executes it; but the conditional \ifx has already been expanded. :)


Note that in LaTeX releases from 2018 onwards the definition of \@ifundefined has been modified so that it no longer defines the tested command to be \relax if it was previously undefined.