nanoaでPerlを学習 その1 package宣言

package hello::start;

nanoaチュートリアル,1行目に書いたのはpackage宣言です.関数ではなくて宣言.この宣言は何をするかというと「ここから書く部分がhello::startというシンボルテーブルに所属するよ」ということを宣言するために書かれています.シンボルテーブルは名前空間ともいいます

最初からなんですが,よくわからなかったら飛ばしてもよいと思います.nanoaの場合パスとこの宣言が一致している(startは省略時に使われる名前,だから/nanoa.cgi/hello/start でもアクセスできます)ので,このパス(/nanoa.cgi/hello/)のプログラムにはこうやって書くおまじないと思いましょう

package宣言の範囲

package hello::start;

この宣言だと,ここから書くことはhello::startというシンボルテーブルに所属するということを意味します.この宣言は同じファイルにある次のpackage宣言までか,ファイルの終わりまで有効です.ですのでhello/start.pmというファイルに書いた内容はすべてhello::startパッケージに所属します

次のpackage宣言が出てくるとそこからは別のパッケージになります.たとえばpackage hello::endとするとそこからはhello::endパッケージになります.ただ,このファイルにhello::endパッケージを書いても,nanoaはパスとファイルとパッケージが一致すること前提なので動作しません

package hello::start;
#ここからhello::startパッケージ

use strict;
use warnings;
use utf8;

use base qw(NanoA);

sub run {
    my $app = shift;
    return $app->render('hello/template/start', {
        user => $app->query->param('user'),
    })
}

#ここまでhello::startパッケージ
package hello::end;
#ここからhello::endパッケージ

use strict;
use warnings;
use utf8;

use base qw(NanoA);

sub run {
    my $app = shift;
    return $app->render('hello/template/end', {
        user => $app->query->param('user'),
    })
}

1;
#ここまでhello::endパッケージ

hello/end?user=lapis25で呼び出してもnot foundになります

2回package宣言する

また,1つのパッケージに対してpackage宣言を2回行うこともできます

package hello::start;

use strict;
use warnings;
use utf8;

use base qw(NanoA);

package hello::end;
print 'end';

package hello::start;

sub run {
    my $app = shift;
    return $app->render('hello/template/start', {
        user => $app->query->param('user'),
    })
}

1;

まったく意味はないですけど,こうやって書いてもふつうに動きます.もちろんprint 'end';は実行されません

暗黙的なpackage宣言

じゃあ,このpackage宣言がひとつも書かれなかったらどうなるの?チュートリアルのファイルはちょっと複雑なのでおいといて,たとえばpkg.cgiという名前で下のコードのように書いたらpackage pkgの前の部分は何なのでしょう?

#! /usr/bin/env perl
#ここのパッケージは???
package pkg;
#ここはhello::startパッケージ

最初のpackage宣言より前の部分は,mainパッケージに所属します.プログラムの先頭にはpackage mainという宣言がなされていると仮定してプログラムは動きます

#! /usr/bin/env perl
#package main;と宣言されてると思いねぇ
#ここのパッケージはmain
package pkg;
#ここはhello::startパッケージ

ちなみにpackage hello::start;の前の行のパッケージはなんなのでしょうか……?ここではひとまず置いておきます

完全な修飾

完全な修飾を使うことで他のパッケージに属する変数を呼び出すこともできます.pkgパッケージの変数$hogeを他のパッケージから呼び出すには,$pkg::hogeというように書くと呼び出せます

#! /usr/bin/env perl
$hoge = "hoge in main package\n";

package pkg;
$hoge = "hoge in pkg package\n";

package main;
print $hoge;
print $pkg::hoge;

このコードはこんな感じで表示されます

$ perl test.pl
hoge in main package
hoge in pkg package

ちなみに$::hogeというように空のパッケージ名を指定すると,mainパッケージを指定したことになります.パッケージには変数だけじゃなくてサブルーチンやハンドルなどにも所属していて,同じようにパッケージの外からアクセスできます

package hello::start;の前の行のパッケージ

答えだけ書くとNanoAパッケージに所属することになります.完全な修飾を使って画面に表示させてみてください.なんで答えだけ書いたかというと正確に理由が書けないからです(笑)nanoa.cgiのコードを追って所属しているだろうということはわかったんですが,機構をちゃんと理解してないので説明ができないということです

さいごに

というわけでpackage宣言でコードをパッケージに所属させることができ,緩いながらもコードを閉じこめることができるようになるわけです(飛躍しすぎ!).パッケージはPerlオブジェクト指向の重要な要素のひとつになっています

スコープとかの話は端折ってますが,よく話題に上がるので調べやすいんではないでしょうか.興味あればどうぞ

間違いの指摘なんかは大歓迎ですのでよろしくお願いします