Variable aliases in Perl

Perlで変数エイリアスをみることができるコードはいくつかあるが,それらはまず二つに大別できる。

真のエイリアスは,別の名前を持つ変数が本当に一つのSVを指しているケースである。Perlの変数はXSからみれば単にSV*型の値であるため,同じSVを指すポインタを同時に複数個作るのは難しくない。

疑似エイリアスは,プロキシとなるMAGIC変数を介してエイリアスの対象となる値と同じように振る舞う値である。MAGICメカニズムを用いてSVに対してGET/SETフックを仕込み,同じ値を持っているように見せかける。当然,エイリアスと元の変数のアドレスは異なる。

真のエイリアスは以下のコードでみられる。

  1. サブルーチンおける@_の要素
  2. foreachイテレータ
  3. map/grepにおける$_
  4. sortにおける$a, $b
  5. 型グロブに対するスカラーリファレンスの代入

疑似エイリアスは以下のコードでみられる。

  1. サブルーチン引数としての存在しないコンテナ要素
  2. 左辺値としてのsubstr(), vec()
  3. 左辺値としてのタイコンテナ要素

特に面白いのが,サブルーチン引数としての存在しないコンテナ要素である。この機能は「@_の要素は引数の値のエイリアスである」という仕様を満たすために実装されたマジックである。

以下,例を示す。

#!perl -w
use strict;
use Data::Dumper;

our %h;
sub f{
  print Dumper \%h; # 1
  $_[0] = 42;
  print Dumper \%h; # 2
  
}

f($h{foo});

__END__

実行結果:

$VAR1 = {};
$VAR1 = {
          'foo' => 42
        };

まず,ハッシュの要素は参照しただけでは作られない。それゆえ,f($h{foo})を実行した直後は%hは空であってしかるべきである(#1)。その一方で,Perlの仕様としては@_の配列要素は引数のエイリアス,すなわち$_[0]と$h{foo}は同じものであるはずなので,$_[0]に対する代入は$h{foo}に対する代入を意味する(#2)。しかし,$h{foo}を参照しただけでは$h{foo}は存在しないことは確認済みである。つまり,$_[0]は存在しないハッシュの要素を参照していることになる。この一見矛盾した挙動を実現するのがSV MAGICによる疑似エイリアスである。

実装はそれほど難しくない。Devel::Peekで確認することができるが,$_[0]の値は実は元のハッシュ(%h)と参照するキー("foo")をプロパティとしてもつマジックオブジェクトである。そのマジックオブジェクトに対するGETは$h{foo}を参照し,SETは$h{foo}を更新するようになっている。これは,Devel::Peekなどで確かめることができる。配列要素についても,キーが整数であるという点を除けば同じメカニズムである。