For some reason lately there has been interest in re-creating the Javascript with function in Perl (despite the fact that ES5 Strict removes with and with has problems of its own). These seem to be focused on using code-rewriting solutions (Source filters and Devel::Declare), which use too much magic for too little syntactical gain, why not something like the following:
package WithBlock;
use strict;
use warnings;
use Scalar::Util 'blessed';
use Exporter qw{import};
our @EXPORT = qw{with dowith};
sub with($&) {
no strict 'refs';
my $pkg = caller(0);
my ($scope, $sub) = @_;
my $class = blessed $scope || $scope;
my @withsubs;
local *{"${pkg}::AUTOLOAD"} = sub {
our $AUTOLOAD;
my ($method) = ($AUTOLOAD =~ m{.+::(.+)$}g);
if (my $isub = $scope->can($method)) {
push @withsubs, $AUTOLOAD;
*{$AUTOLOAD} = sub {
unshift @_, $scope;
goto &$isub;
};
goto &{$AUTOLOAD};
}
die "Undefined subroutine &$AUTOLOAD called"
};
$sub->();
foreach my $sub (@withsubs) {
undef *{$sub};
}
}
sub dowith(&$) {
@_[0, 1] = @_[1, 0];
goto __PACKAGE__->can('with');
}
Admittedly you don't get the 'nice' syntax of with ($object) { code block } you have to settle for with $object, sub { code block } or dowith { code block } $object. WithBlock only works for functions of $object that aren't already defined in your current scope.
package WithBlock;
use strict;
use warnings;
use Scalar::Util 'blessed';
use Exporter qw{import};
our @EXPORT = qw{with dowith};
sub with($&) {
no strict 'refs';
my $pkg = caller(0);
my ($scope, $sub) = @_;
my $class = blessed $scope || $scope;
my @withsubs;
local *{"${pkg}::AUTOLOAD"} = sub {
our $AUTOLOAD;
my ($method) = ($AUTOLOAD =~ m{.+::(.+)$}g);
if (my $isub = $scope->can($method)) {
push @withsubs, $AUTOLOAD;
*{$AUTOLOAD} = sub {
unshift @_, $scope;
goto &$isub;
};
goto &{$AUTOLOAD};
}
die "Undefined subroutine &$AUTOLOAD called"
};
$sub->();
foreach my $sub (@withsubs) {
undef *{$sub};
}
}
sub dowith(&$) {
@_[0, 1] = @_[1, 0];
goto __PACKAGE__->can('with');
}
Admittedly you don't get the 'nice' syntax of with ($object) { code block } you have to settle for with $object, sub { code block } or dowith { code block } $object. WithBlock only works for functions of $object that aren't already defined in your current scope.
Leave a comment