呼び出し元のコンテキストで eval する方法

String::TT なんかがそうですけど、呼び出し元の変数を使いたい、ということがときどきあります。たとえば、

sub hello_to_alice {
  my $user = "Alice";
  say_hello();        # "Hello to Alice!" と言ってほしい
}

って書きたいとか、そういうケースですね。結論から言うと、以下のように say_hello 関数を定義すれば可能です。こんな感じ。

sub say_hello {
  @_ = ('print "Hello to $user!\n"', undef);
  goto &DB::eval_in_parent_and_return;
}

package DB;

sub DB::eval_in_parent_and_return {
  my ($expr, $retvar) = @_;
  eval("package " . (caller(0))[0] . ";$expr");
  die $@ if $@;
  $retvar;
}

eval_in_parent_and_return 関数で、呼び出し元のコンテキストで print 文を eval させています。すばらしいですね!

PadWalker と比較すると以下のような感じでしょうか。

+ pureperl で書ける
+ アクセサを使うのではなく perl expression を実行できる
− eval なので遅いかも

何かの折に使えるかもしれません。

ちなみになんでこんなことができるかと言うと、perl デバッガを実装するために、DB パッケージ内での eval は呼び出し元のコンテキストで実行されるようになっているからです。