camel

ついでなので、perlのpackageについて。

odz buffer - Perl は 1ファイルに複数のパッケージを書けるはず・・・
違いますよ、と突っ込もうと思ったけど、自信がないので確認(おぃ)。

Perl5におけるpackageというのは、単なるnamespaceの宣言に過ぎず、それ以上でもそれ以下でもありません。package Foo;というのは、「このlexical scopeにおけるnamespaceはFooである」という宣言なのです。なので、

ちなみに HTTP::Proxyの SYNOPSIS なんかみると、
{
    package FilterPerl;
    use base qw( HTTP::Proxy::BodyFilter );
    # snip
}
な感じでブロック中でクラス定義してたりする。なぜかは知らない。

というのは、{}の中のみがFilterPerlという意味になります。これは以下を見ればすぐにわかるでしょう。

use strict;
use warnings;
{
    package Foo;
    warn __PACKAGE__;
}
warn __PACKAGE__;
package Baz;
warn __PACKAGE__;
package main;
warn __PACKAGE__;

実行すると、

Foo at pkg.pl line 5.
main at pkg.pl line 7.
Baz at pkg.pl line 9.
main at pkg.pl line 11.

となります。元来packageというのはファイルとは関係なしに存在する概念なのです。

ただし、require Foo::Barおよびuse Foo::Barでは、様相が少し違います。この二つにおいては、「Foo/Bar.pmというファイルがロードされていなければ、@INC中のDirectoryにFoo/Bar.pmにそれを探しに行く」という意味になります。ロードされているかどうかという判定は、$INC{'Foo/Bar.pm'}が存在するかでなされます。

% perl -le 'print "$_ => $INC{$_}" for keys %INC'
% perl -MCGI -le 'print "$_ => $INC{$_}" for keys %INC'
warnings/register.pm => /usr/local/lib/perl5/5.8.8/warnings/register.pm
Carp.pm => /usr/local/lib/perl5/5.8.8/Carp.pm
vars.pm => /usr/local/lib/perl5/5.8.8/vars.pm
strict.pm => /usr/local/lib/perl5/5.8.8/strict.pm
Exporter.pm => /usr/local/lib/perl5/5.8.8/Exporter.pm
constant.pm => /usr/local/lib/perl5/5.8.8/constant.pm
warnings.pm => /usr/local/lib/perl5/5.8.8/warnings.pm
CGI/Util.pm => /usr/local/lib/perl5/5.8.8/CGI/Util.pm
overload.pm => /usr/local/lib/perl5/5.8.8/overload.pm
CGI.pm => /usr/local/lib/perl5/5.8.8/CGI.pm

よって、use Foo::Barは、実はpackage Foo::Barとは関係ないのです。以下も問題なく動いてしまいます。

% cat Foo/Bar.pm
package Baz;
warn __PACKAGE__;
1;
% perl -MFoo::Bar -le 'print "$_ => $INC{$_}" for keys %INC'
Baz at Foo/Bar.pm line 2.
Foo/Bar.pm => Foo/Bar.pm

もっとも、これではわかりにくいので、たいていの場合はFoo/Bar.pmの最初にpackage Foo::Bar;という宣言を入れておくわけです。

一見非直感的ですが、これのおかげでこんな技も使えます。

Dan.pm
use strict;
use warnings;
use utf8;   # これを宣言しておく必要あり
package 小飼;
sub 弾 {
    my $クラス = shift;
    bless { @_ }, $クラス
};
for my $メソッド (qw/空気/){
    no strict 'refs';
    *{$メソッド} = sub{
        my $俺 = shift;
        $俺->{$メソッド} = shift if @_;
        $俺->{$メソッド};
    };
};
1;
dan.pl
use strict;
use warnings;
use utf8;
use Dan;
use Data::Dumper;
binmode STDOUT => ':utf8';
sub say { print @_, "\n" };
my $私 = 小飼->弾('空気' => '嫁');
warn Dumper $私;
say $私->空気;
1;
% perl dan.pl
$VAR1 = bless( {
                 "\x{7a7a}\x{6c17}" => "\x{5ac1}"
               }, '小飼' );
嫁

Dan the Perl Monger