perlでモジュールのimportとunimport

perlでモジュールをインポートする時に何気なく使ってるuse Hogeですが、調べてみると少し思ってたのと違ってたので、調べた事をメモしておきます。

use Hogeは実は標準関数useの呼出しを行っていて、その内容は以下と同等です。

BEGIN {
    require Hoge;  # 中で更にdo Hoge.pmを呼び出す
    import Hoge;   # Hoge::importを実行
}

require Hogeは対応する.pmファイルを読み込み、importはHoge::importを呼び出します。requireされた時点でロードは完了して、Hoge.pmに定義されている関数等は使用可能です。じゃあimportで何をしてるかというと、読み込み元パッケージの名前空間にHoge.pm内の関数に対するエイリアスを定義する為に使われる事が多いようです。

逆にモジュールをunimportしたい場合はno Moduleを使います。no ModuleとするとModule::unimportが呼び出されます。no strict ‘refs’等はよく見かけますね。ここで注意したいのはno Moduleを呼び出したからといってModuleが無効化されるとは限らないという事です。あくまでもModule::unimportが呼ばれるだけです。またunimportが実装されていないモジュールに対しては何も起こりません。

という事は一度importしてしまうとunimportできない場合も多いので、そもそもimportしたくないという状況がたまにあります。そういう時はどうすれば良いかというと、use Hoge()を使います。Hogeの後に括弧が付くとimportが呼ばれず以下と等しくなります。

BEGIN {
    require Hoge;
}

どういう時に使うのかというと、別々のモジュールから同じ名前の識別子がimportされてしまうのを回避するのに使えます。例えばthreadsとCoroにはasyncという関数が定義されていて、同一パッケージ内で一緒にuseするとエラーがでるわけですが、use threads()、use Coro()とする事でエラーを回避できます。他にも使い道があるかもしれませんが、今のところ思いつきません。あったら教えて下さい。

#!/usr/bin/perl

package import_test;
use threads;
use Coro;       # ここでエラーが出る。エラー内容は以下。
# Prototype mismatch: sub import_test::async (&;@) vs (&@) at /Library/Perl/Updates/5.8.8/Exporter.pm line 67.

package main;
use threads();  # importさせなければエラーは出ない
use Coro();

ところで、useはBEGINブロックに置き換わると等しいと書きましたが、本当かどうか解らなかったので、確認したいなぁと思って探しているとDeparseというモジュールを使うと良いと解りました。試しに以下のコードをperl -MO=Deparse,-x5 test.plとしてみましょう。

use threads();   # importなし
use Coro;         # importあり

そうすると以下の様な出力が得られます。Deparseはスクリプトの解析結果を出力するモジュールで、-xオプションで指定する出力レベルを上げると、より内部構造に近い形でスクリプトを見る事が出来ます。実際にBEGINブロックが得られていて、threadsのimportが呼ばれず、Coroのimportが呼ばれている事も確認できますね。

sub BEGIN {
    require threads;
    do {
        ()
    };
}
sub BEGIN {
    require Coro;
    do {
        'Coro'->import
    };
}

まとめ

  • perlのuseは他の言語とは違って、標準関数の1つ。perldoc -f use参照
  • use Module、no Moduleされた時の挙動もモジュールに依存してる。
  • なんか変だと感じたらとりあえずDeparse、その後モジュールのimportのコードを読む。

Leave a Reply

Your email address will not be published. Required fields are marked *