evalとpackage。

wakaponさんのよく☆ある☆ふつーの備忘帳:packageの件について
http://pub.ne.jp/wakapon/?entry_id=1981011


こちらの記事の最後にあったいろいろなおまけ、
自分でもいくつか考えてやってみたのでまとめ。

eval 'package HOGE; print __PACKAGE__; $num = 123;';
our $num = 1234; # こいつはmainに属する
eval 'print $HOGE::num';

元記事のおまけ5の派生。
出力は

HOGE123

となった。

packageの効果範囲はmyと似ていて、「宣言したスコープから抜ける」まで。
evalに与えた文字列は「ひとつのブロックの中」として扱われる。

なので、1行目の「$num = 123;」はHOGE空間で実行されることになり、
$HOGE::numに値が存在することとなった。


これは「同じeval中で代入したから」ではなく「package宣言と同じスコープで代入したから」である点が重要。
つまり、

eval '{ package HOGE; print __PACKAGE__; } $num = 123;';
our $num = 1234; # こいつはmainに属する
eval 'print $HOGE::num'; # 何も出力されない

ということになる。
package宣言を一つ深いスコープに持っていった。
すると「$num = 123;」はHOGE空間を抜けた後、main空間で実行されることになる。

eval '{ package HOGE; print __PACKAGE__; } $num = 123;';

print $num; # 123

ってこと。


あとevalとは関係ないけど、package宣言は同じ空間を複数回宣言することもできる。

package HOGE;

$num = 1;

package main;

print $HOGE::num; # 1
print $HOGE::foo; # undefined

package HOGE;

$foo = 2;

package main;

print $HOGE::num; # 1
print $HOGE::foo; # 2

要するに「HOGEって部屋に出たり入ったり」してるってこと。
2回目以降の宣言で上書きされるわけではなく、追記。