Module to encapsulate a scope and eval code in it later or rebind a closure using it.

Update: fix private

package ScopeSaver; use strict; use warnings; use Carp; use B::Deparse; =pod $ss_obj = eval ScopeSaver->new(lexical => [qw/$x %y/], global => [qw/$q @r/], private => [qw/$t $u/]); ... # eval code in saved scope $ss_obj->scope_eval(" code "); # rebind closure into saved scope $coderef = $ss_obj->rebind($coderef); Encapsulates the current scope into an object, which can then evaluate code using the saved scope or rebind a closure into it. You can control some aspects of how variables in evalled code interact with the scope with the lexical, global, and private parameters, which each take a ref to an array of simple variable names. To be able to access lexicals in the scope even after their normal scope has ended, list them in the lexical parameter. To have evaled code access globals instead of the lexicals in the scope of the same name, list them in the global parameter. To have evaled code use lexicals private to each eval instead of lexicals in the scope or globals, list them in the private parameter. =cut sub new { my $class = shift; my %vars = @_; my $str = "bless sub {\n"; $str .= "(".join(",",@{$vars{lexical}}).") if 0;\n" if $vars{lexic +al}; $str .= "our (".join(",",@{$vars{global}}).");\n" if $vars{global} +; $str .= "my (".join(",",@{$vars{private}}).");\n" if $vars{private +}; $str .= "eval \$_[0]\n}, \"$class\";\n" } sub scope_eval { my $self = shift; $self->($_[0]); } sub rebind { my $self = shift; UNIVERSAL::isa($_[0], "CODE") or croak "rebind expected coderef"; $self->("sub ".B::Deparse->new->coderef2text($_[0])); } 1;

Replies are listed 'Best First'.
Re: Rebinding closures by scope
by ysth (Canon) on Dec 17, 2003 at 19:38 UTC
    Some sample uses:
    use ScopeSaver; use strict; use warnings; our $ss; { my $x = 1; { my $y = 2; my $z = 3; $ss = eval new ScopeSaver(lexical => ['$y']); } # $x doesn't need to be listed as lexical (though it would be harmle +ss) # since its scope hasn't ended at this time $ss->scope_eval('print "x is $x\n"'); # $y is still available because it was listed $ss->scope_eval('print "y is $y\n"'); # $z is no longer available but wasn't listed to preserve $ss->scope_eval('print "z is ", (defined $z?$z:"undef"), "\n"'); } { my $closure; { my $x = "original"; $closure = sub { print "in $x closure\n" }; } { my $x = "rebound"; $ss = eval new ScopeSaver(lexical => ['$x']); } # in original closure: &$closure; # rebound closure: $closure = $ss->rebind($closure); &$closure; } { our ($x,$y) = 613..614; { my ($x,$y) = 666..667; $ss = eval new ScopeSaver(global => ['$x'], private => ['$y']); } # $x will be the global, not the lexical: $ss->scope_eval('print "x is $x\n"'); # $y will be a freshly allocated lexical for each eval, # not the global or the outside lexical: $ss->scope_eval('print "y is ", (defined $y?$y:"undef"), "\n"'); }