PerlIO::code

Perlのopen()はファイル名に相当する引数がリファレンスだと特別扱いする。以下のようなことが出来るのはそのためだ。

my $scalar = 'foo';
open my $fh, '<', \$scalar;
print <$fh>; # => "foo"

これはスカラーリファレンスに対してPerlIO::scalarを自動的にロードし,レイヤとして設定するためだ。この処理を行うのがperlio.cのPerlIO_layer_from_ref()だが,この関数をみるとスカラーリファレンスだけではなく他のリファレンスも特別扱いされることがわかる。

/* perlio.c, v5.8.8 */
static PerlIO_funcs *
PerlIO_layer_from_ref(pTHX_ SV *sv){
    if (SvTYPE(sv) < SVt_PVAV)
	return PerlIO_find_layer(aTHX_ "scalar", 6, 1);
    switch (SvTYPE(sv)) {
    case SVt_PVAV:
	return PerlIO_find_layer(aTHX_ "Array", 5, 0);
    case SVt_PVHV:
	return PerlIO_find_layer(aTHX_ "Hash", 4, 0);
    case SVt_PVCV:
	return PerlIO_find_layer(aTHX_ "Code", 4, 0);
    case SVt_PVGV:
	return PerlIO_find_layer(aTHX_ "Glob", 4, 0);
    }
    return NULL;
}

PerlIO_find_layerの最後の引数はモジュールのロードを試みるかどうかのフラグなので,スカラー以外のリファレンスのときは対応モジュールをロードしたりはしないようだ。しかし,モジュールがロードされていればopen()にレイヤを指定する必要はない。

このことを利用して,PerlIO::codeというレイヤモジュールを書いてみた。

use PerlIO::code;
open my $in, sub{ my $s = <>; $s =~ s/foo/bar/g; $s };

tieやPerlIO::viaと比較するとかなり機能は限られているが,その分簡単に使える。しかし,PerlIOインターフェイスを直接使用するから高速かというとそうでもなく,tieインターフェイスを使うよりも遅い。

それにしても,コードリファレンスならともかく,配列やハッシュやグロブを特別扱いすることに意味はあるのだろうか。単に一貫性の問題だろうか。そうだとすると名前に一貫性がないが。このように,不可解な仕様がたくさんあるのがまたPerlの魅力でもあるのだが。