perlpragma - ユーザープラグマの書き方
プラグマは、strict や warnings のように、コンパイル時や実行時の Perl の
振る舞いにある種の影響を与えるモジュールです。
Perl 5.10 から、プラグマは組み込みのものに制限されません; レキシカル
スコープ内のユーザー関数の振る舞いを変えるユーザープラグマを
作れるようになりました。
例えば、オーバーロードされた算術演算子を実装するクラスを作る必要が
あるとして、use integer; のように働く独自のプラグマを提供したいと
します。
以下のようなコードで
    use MyMaths;
    
    my $l = MyMaths->new(1.2);
    my $r = MyMaths->new(3.4);
    
    print "A: ", $l + $r, "\n";
    
    use myint;
    print "B: ", $l + $r, "\n";
    
    {
        no myint;
        print "C: ", $l + $r, "\n";
    }
    
    print "D: ", $l + $r, "\n";
    
    no myint;
    print "E: ", $l + $r, "\n";
以下のように出力されます
    A: 4.6
    B: 4
    C: 4.6
    D: 4
    E: 4.6
つまり、use myint; が有効のときには家宝演算子は整数に強制され、
一方デフォルトではそうではありませんし、デフォルトの振る舞いは
no myint; で復元されています。
MyMaths パッケージの最低限の実装は以下のようなものです:
    package MyMaths;
    use warnings;
    use strict;
    use myint();
    use overload '+' => sub {
        my ($l, $r) = @_;
        # Pass 1 to check up one call level from here
        if (myint::in_effect(1)) {
            int($$l) + int($$r);
        } else {
            $$l + $$r;
        }
    };
    
    sub new {
        my ($class, $value) = @_;
        bless \$value, $class;
    }
    
    1;
import が呼び出されないようにユーザープラグマ myint を空リスト
() 付きで呼び出す方法に注意してください。
Perl コンパイラとの相互作用はパッケージ myint の内側で起こります:
    package myint;
    
    use strict;
    use warnings;
    
    sub import {
        $^H{myint} = 1;
    }
    
    sub unimport {
        $^H{myint} = 0;
    }
    
    sub in_effect {
        my $level = shift // 0;
        my $hinthash = (caller($level))[10];
        return $hinthash->{myint};
    }
    
    1;
他のモジュールと同様、プラグマもモジュールとして実装され、use myint; は
以下のようになり:
    BEGIN {
        require myint;
        myint->import();
    }
no myint; は以下のようになります:
    BEGIN {
        require myint;
        myint->unimport();
    }
従って import と unimport のルーチンはユーザーコードの
コンパイル時 に呼び出されます。
ユーザープラグマは、マジカルなハッシュ %^H に書き込むことで状態を
保持するので、これらの二つのサブルーチンはこれを操作します。
%^H の状態情報は構文木に保管され、caller() から返されたリストの
インデックス 10 の要素としてして実行中に取得できます。
例のプラグマでは、返されたものはユーザースクリプトのプラグマの値を
見つけるために上がっていく呼び出しフレームの数をパラメータとして受け取る
in_effect() ルーチンでカプセル化されます。
これはユーザーのスクリプトの各行が呼び出されるときに $^H{myint} の
値を決定するために caller() を使うので、オーバーロードされた加算を
実装しているサブルーチン内で正しい意味論を提供します。
構文木はスレッド間で共有されます。
つまり、構文木はそれを作った特定のスレッド(従ってインタプリタ実体)よりも
長生きする可能性があると言うことです。
従って、真の Perl スカラを構文木に保管することが出来ません。
圧縮形式を使う代わりに、整数(符号付きと符号なし)、文字列、undef の
いずれかの値のみを保管できます - リファレンスと浮動小数点数は
文字列化されます。
もし複数の値や複雑な構造体を保管する必要があるなら、例えば pack などを
使って直列化するべきです。
%^H からのハッシュキーの削除は記録され、いままで通り exists を使うことで
値が undef でキーが存在することと区別できます。
リファレンスをcaller 経由で取得して変換し直した整数としてデータ構造に
保管しようとしてはいけません; これはスレッドセーフではないからです。
アクセスはロックなしでの構造体に対してであり(これは Perl のスカラに対しては
安全ではありません)、構造体がメモリリークするか、作成したスレッドが終了時に
解放され、もしもし他のスレッドが長生きしすぎると、構文木が削除されたものを
参照することになります。