Use -w in applications, use warnings in modules

警告のためには -w ではなく use warnings を使うべき,という意見がある*1

しかしそれはある意味では正解だが,ある意味では間違いである。正しくは「アプリケーションでは #!行に-wと書き,モジュールではuse warningsを使う」である。

以下のコードではそれを示す。
(1)

$ perl -MFile::Spec -E 'say File::Spec->join("foo", undef)'
foo/
$ 

(2)

$ perl -MFile::Spec -wE 'say File::Spec->join("foo", undef)'
Use of uninitialized value $file in concatenation (.) or string at ...
foo/
$ 

(3)

$ perl -MFile::Spec -E 'use warnings; say File::Spec->join("foo", undef)'
foo/
$ 

(1), (2), (3)の例のうち,あるべき警告が出ているのは(2)の-w指定のケースのみである。つまり,use warningsの効果はレキシカルなので,読み込むモジュールにとっては効果がないのだ。そしてFile::Specは内部でuse warningsを指定していない。

したがって,すべてのモジュールがuse warningsを指定しているのでなければ安全のために-wを指定したほうがいい,ということになる。
一方モジュールでは,グローバルな状態(-w)に何らかの仮定をするのは望ましくないので,常にuse warningsを指定したほうがよい。

つまりPerlの警告メカニズムを有効に使うためには,アプリケーションでは #!行に-wと書き,モジュールではuse warningsを使うべき,ということになる。

(追記)
コメントで反対意見をいただいた。要旨としては「モジュール内でuse warningsをしていないのは(1)後方互換性のため,あるいは(2)意図的なポリシーとしてであり,いずれにせよ意図的なものなので,-wによってその意図を破壊してはならない」ということだと解釈した。以下はその点について反論したい。

私は警告モードについてモジュール製作者の意図を汲む必要はないと考えている。すなわち,(1)の後方互換性のためだとすれば,モジュール作者はメインプログラムで-wが使われることを想定していると考えられるため,アプリケーションでは-wを指定するべきだろう。(2)の意図的なものだとすれば,すべての警告を無視するのではなく,無視したい警告カテゴリに対してno warningsを使うべきだろうし,そうでないとしても,モジュールの使用者側で警告を回避する方法はあるのが普通だ*2。いずれにしても-wによってその意図が破壊されることはない。

たとえば本文では非常によくつかわれる標準モジュールとしてFile::Specの例をあげたが,File::Specが引数のチェックしないというのは確かにFile::Specのポリシーかもしれない。しかしこれは,-wを前提としたうえで,使用する側が引数をチェックするべきだという意味に他ならない。5.6.0より前は-wを前提にしてプログラムを書くのが普通だったのだから。

また,例ではuninitialized警告を扱ったが,警告はそれだけではない。ある特定の警告がうるさいからといって-wを指定しないのはベストプラクティスとは言えない。

*1:「サンプルコード」のほうではそもそも言及していない

*2:ないとすればおそらくそれはそのモジュールのバグであろう