マクロツイーター

はてダから移行した記事の表示が崩れてますが、そのうちに直せればいいのに(えっ)

\def か \newcommand か(ZR氏の場合)

例の“二文字フォント命令”に関する喧騒から派生する形で、\newcommand\def のどちらを使うべきか」TeX 界隈でチョットだけ話題になったようである。この記事では、「某 ZR 氏はどう使い分けているか」という(他人にとってはどうでもよさそうな)話題を扱う。*1

LaTeXのコードを書いている場合

この場合は何も迷うことはない。\defLaTeX の命令ではないので、必然的に \newcommand を使う*2ことになる。

TeX言語のコードを書いている場合

TeX 言語でのプログラミングを前提としている場合は、\newcommand を「\@ifdefinable 判定付の \def の省略形」と解釈する。例えば、次のような \newcommand を用いたマクロ定義は:

\newcomamnd\framedparbox[2]{%
  \fbox{\parbox{#1}{#2}}%
}

以下のものと等価になる:

\@ifdefinable{\framedparbox}{%
  \long\def\framedparbox#1#2{%
    \fbox{\parbox{#1}{#2}}%
  }
}

このように解釈しておくと、“\newcommand が使えない場合”(\edef を使いたい、区切り付引数をもつマクロを定義したい、など)やマクロ以外の定義(\newskip((ちなみに、LaTeX\newlength は「\@ifdefinable 判定付の \newskip」である。))や \chardef\let など)も統一的に扱える。要するに問題は\@ifdefinable 判定を行うか行わないか」である。\@ifdefinable は「制御綴(control sequence)が定義済であるかを判定する」ための補助マクロなので、この使用の有無については、「今自分が定義しようとしたマクロの制御綴が既に定義済だった場合」に何が起こることを期待するのか、により決定される。

定義済ではあり得ない(と想定する)制御綴

すなわち、制御綴名が“ユニーク”(他の人が使わないほど十分に複雑な名前)であると想定している場合である。当然、\@ifdefinable 判定は不要なので行わない。すなわち、\newcomamnd が使用可能な状況でも \def を用いる。

例えば、“bxyz”を名前空間としているパッケージで、内部マクロ“\bxyz@set@width”(名前空間を付けているので“ユニーク”である)を定義したい場合は \def を用いる。

他のパッケージの“ユニーク”な制御綴

他のパッケージ(など)の“ユニーク”な制御綴に対して定義を行う場面は、当然、当該のパッケージの動作に修正を加える(パッチする)のが目的であるときに限られる。当該の制御綴は定義済である(はずな)ので、\@ifdefinable 判定は行ってはいけない。

例えば、geometry パッケージの内部マクロ \Gm@detectdriver にパッチを当てたい場合は \def で再定義を行う。

“非ユニーク”な非公開の制御綴

“ユニーク”でない、ということは、誰でもその制御綴を使う可能性があるということである。なので、あるパッケージがその制御綴を定義したとしても、後で別のパッケージに(全く無関係な定義により)上書きされるかも知れない、ということは想定の範囲内である。つまり、そもそも定義済であることを気にする必要がないわけだから、このような制御綴を \@ifdefinable 判定を行う場面はあり得ない。

%% "非ユニーク"な制御綴 \@name を使ったコードの例
\def\@name{auto}% \newcommand でなく \def を使う
\ifx\bxyz@width@val\@name
  ......
\fi
% この後 \@name が誰かに上書きされても全然構わない
公開(ユーザ命令)の制御綴

“公開"の、すなわち @ を含まない名前の制御綴については非公開(@ 付)のものとは異なる事情がある。これらがユーザ(文書作成者)が記述するものであるから、あまり複雑な名前(特に“bxyz”のような意味不明な名前空間の接頭辞が付いたもの)を使うわけにはいかない。従ってどうしても“それほどユニークでない”制御綴、つまり他のパッケージと衝突する制御綴を使う必要が出てくる。

例えば \linegaplimit という公開の制御綴を定義したいとする。ここでもしこの制御綴が別のパッケージ A によって既に定義されていた場合、どうすべきだろうか? 自分が定義しようとしているものは「パッケージ A の \linegaplimit のパッチ」ではない。だからここでもし定義を強行してしまうと、パッケージ A の(少なくとも一部の)機能が破壊されてしまう。逆に自分の定義を行わないと、自分のパッケージ(とか)の仕様が満たせなくなる。((「自分が定義する方」については、「代わりに別の制御綴を使う」という選択も可能である。もちろん、この場合は「そういう選択が行われうる」ことを自分のパッケージの仕様として明記すべきである。例えば、pxrubrica パッケージでは、\ruby という、“特に衝突する可能性が大きい”公開の制御綴について、「もし \ruby が定義済だったら代わりに \jruby を使う」という代替仕様を定めている。))どちらにしても「ユーザの意図通りにならない」可能性を含む異常事態であるので、エラーを出して異常を知らせるべきである。

従って、公開の制御綴の場合は \@ifdefinable による判定は必要不可欠である。「公開の制御綴」で定義されるものは“LaTeX の文法に従った書式のマクロ”であることが多く、その場合は \newcommand を使うことになる。

まとめ

“非公開(@ 付)のもの”または“他人のものへのパッチ”である場合は \def、それ以外は \newcommand という使い分けになる。「\def\newcommand か」というのは、本質的には「\@ifdefinable するかしないか」ということなのである。

*1:もちろん、某 ZR 氏の方針が他のものよりも優れていると主張するものではない。

*2:あるいは xparse などの「LaTeX のマクロ定義」を拡張するためのパッケージが提供する機能を利用する。