our変数の動作とか

Perlのmy, local, ourの違いについて,はてブ方面で盛り上がっていましたので,私も5年ぶりぐらいにPerlを触ってみましたが,ourの検証でのっけからはまりました.

まず,「ourはレキシカル変数だ」と思って,次のようなコードを書きました.

#!/usr/local/bin/perl
use strict;
use warnings;

package Mypackage;
our($foo) = 'our in toplevel';

sub bar {
    sub { $foo .= '!'; print $foo, "\n" }
}

my $closure;
{
    my($foo) = 'my in bare block';
    $closure = bar();
    $closure->();
}
$closure->();

print $foo, "\n";

これはok.次のように,期待通りに動きます.

% ./lexical1.pl
our in toplevel!
our in toplevel!!
our in toplevel!!

ここで,無名ブロック(クロージャ)の中のmyをlocalに変更してみる.動作に支障はないはず.

#!/usr/local/bin/perl
use strict;
use warnings;

package Mypackage;
our($foo) = 'our in toplevel';

sub bar {
    sub { $foo .= '!'; print $foo, "\n" }
}

my $closure;
{
    local($foo) = 'local in bare block';  # 変更
    $closure = bar();
    $closure->();
}
$closure->();

print $foo, "\n";

実行結果

% ./lexical2.pl
local in bare block!
our in toplevel!
our in toplevel!

あれ? クロージャの中の$fooがダイナミックスコープ*1になってる.

うーん.これって,our($foo)の,"$foo"という変数名が,local($foo)のところで上書き(シャドウ化)されたってこと? それで,bar()の中に着いた段階では,レキシカル変数であるはずのour($foo)は全然見えないから,変数の束縛も行われなかったってこと?

それならば,と,さらに1行追加.

#!/usr/local/bin/perl
use strict;
use warnings;

package Mypackage;
our($foo) = 'our in toplevel';

sub bar {
    my($foo) = 'my in bar';  # 追加 ...(1)
    sub { $foo .= '!'; print $foo, "\n" }
}

my $closure;
{
    local($foo) = 'local in bare block';
    $closure = bar();
    $closure->();
}
$closure->();

print $foo, "\n";

こちらは,以下のように期待通りの結果.しかし,トップレベルのour($foo)が参照できないことは変わらないので全然うれしくない.

% ./lexical3.pl
my in bar!
my in bar!!
our in toplevel

(1) のところで,our($foo)をどうにか参照できないか,と,my($foo) = $Mypackage::fooとか,苦し紛れにlocal($foo) = $fooとかしてみるが,いずれもlocal($foo)がしゃしゃり出てきて全然だめ.

Perlって難しいなぁ.

*1:この日記を書いた時点では,このような動きを何と呼ぶのかわからなかったのだが,shallow bindingと呼ぶのが正しいらしい.