理系学生日記

おまえはいつまで学生気分なのか

Sub::Exporter

Exporter.pm のモダン版と言われている Sub::Exporter の POD を初めて読んでみました。

POD の中で Sub::Exporter を使う "Biggest benefit" と言われているのが Export する対象(coderef) を操作できることです。POD の例で分かりやすいのは以下のようなものです。

Data::Anaylyze に 3 つの引数を取る関数があったとして、

use Data::Analyze qw(analyze);
my $value = analyze($data, $tolerance, $passes);

Exporter.pm ができるのは、Data::Analyze を使うパッケージに対してこの analyze をそのままエクスポート(名前空間にコピー)することです。しかし、Sub::Exporter は、Data::Analyze に手を入れることなく、analyze をカリー化したものをロードすることができます。

use Data::Analyze
    analyze => { tolerance => 0.10, passes => 10, -as => analyze10 },
    analyze => { tolerance => 0.15, passed => 50, -as => analyze50 };

my $value = analyze10( $data );

多数の関数をクロージャ的に生成することもできます。
以下のような形で、{ key1 => coderef1, key2 => coderef2 } というような hashref を返す高階関数(?)を Sub::Exporter の groups に渡すと、これを使う側のモジュールの名前空間に key1、key2 がエクスポートされます。

# 呼び出される側
package Kiririmode;
use strict;
use warnings;
use Sub::Exporter -setup => {
    groups => { many => \&build_kiririmodes }
};

sub build_kiririmodes {
    my ($class, $group, $arg) = @_;
    my @range = @{ $arg->{range} };

    my $ret;
    for my $num (@range) {
        $ret->{"kiririmode$num"} = sub {
            print "kiririmode$num\n";
        }
    }
    $ret;
}

1;
#!/opt/local/bin/perl
# 呼ぶ側
use strict;
use warnings;
use Kiririmode ':many' => { range => [ 1..10 ] };

kiririmode1();    # prints kiririmode1
kiririmode10();   # prints kiririmode10

もちろん、Exporter.pm 的な使い方もできます。例えば、Exporter で言う tag を使った例。

package Kiririmode;
use strict;
use warnings;
use Sub::Exporter -setup => {
    exports => [ qw(sub1 sub2 sub3 sub4) ],
    groups  => { subs => [ qw(sub1 sub2 sub3) ] }
};

sub sub1 {
    print "sub1\n";
}
sub sub2 {
    print "sub2\n";
}
sub sub3 {
    print "sub3\n";
}
sub sub4 {
    print "sub4\n";
}

1;
#!/opt/local/bin/perl
use strict;
use warnings;
use Kiririmode ':subs';

sub1();  # prints sub1
sub2();  # prints sub2
sub3();  # prints sub3
sub4();  # warns Undefined subroutine &main::sub4 called at test-subexporter.pl line 9.

collectors 等を使うと呼び出し側でもっと柔軟にモジュールを使うことができますし、魅力的なモジュールだなって思いました。