Perl block with lexically-scoped method delegation
About a year ago I came across this interesting Ruby blog post Keyword With.
This is something I always wanted in Perl and immediately marked a note in my list of blog/CPAN ideas with “Devel::Declare and with statement” (with side note that “using” might be more appropriate so not to clash with Moose roles sugar).
Unsurprisingly this just languished in my ever increasing list π¦
However last month I had a few hours spare one weekend so pulled out my laptop and rummaged through my list and decided to see how difficult it would be to implement “with”.
Unfortunately I kept hitting brick walls with local scoping. I came to conclusion that I may have to use eval or even go down a different track using an AUTOLOAD solution.
I left that weekend experiment with at least a very basic AUTOLOAD proof of concept:
use 5.012; use warnings; { package FileStuff; use Moose; has name => ( is => 'rw', isa => 'Str' ); sub save { say "save ", $_[0]->name } sub del { say "delete ", $_[0]->name } sub mv { my ($self, $to) = @_; say "rename ", $self->name, " to $to"; $self->name( $to ); } sub say { say "Method say: ", @_ } } my $f = FileStuff->new( name => 'foobar' ); # using $f {} { local *AUTOLOAD = sub { our $AUTOLOAD; my $name = (split '::', $AUTOLOAD)[-1]; die "method $name NOT FOUND!" unless $f->can( $name ); $f->$name( @_ ); }; # my "DSL" stuff! save(); # $f->save(); mv( "barbaz" ); # $f->mv( "barbaz" ); del(); # parenthesis are always required say( "builtin say" ); # builtin wins over $f->say }
I wasn’t sure if this would ever evolve into a robust solution. However I found it reassuring to note that other people had similar problems trying to use local scoping in similar way when I came across this Perlmonks post A useful use of goto a few days later. So this gave the AUTOLOAD approach some merit.
Well chances are this blog post or any additional development may never made it any further. What changed that was an email from chocolateboy this morning pointing me at his new Scope::With CPAN module.
Blimey… Father Christmas had delivered my xmas pressie early and without even seeing my xmas list π
chocolateboy’s first example of Scope::With even shows how it works with my Builder module:
use Builder; use Scope::With; my $builder = Builder->new(); my $xml = $builder->block('Builder::XML'); with ($xml) { body( div( span({ id => 1 }, 'one'), span({ id => 2 }, 'two'), ) ); } say $builder->render();
Which is exactly why I wanted a “with” statement in Perl and it looked like chocolateboy also felt the same!
The documentation even gave an “using” example:
use Scope::With qw(using); using ($xml) { div( span( ... ), span( ... ), ); }
Getting worried now someone was reading my mind π
This is truly awesome stuff. chocolateboy has the habit of producing game changing CPAN modules. For eg. autobox and Method::Lexical. To me Scope::With is potentially another one that falls into this lofty category.
I’m deeply honoured to find my Builder module being referenced and also glad to see that a “with” statement using Devel::Declare and AUTOLOAD isn’t such a delusional idea π
So many thanks to chocolateboy for writing Scope::With. Keep up the good work.
/I3az/
PS. chocolateboy’s Scope::With docs also references a prior art module with by Vincent Pit. This module achieves similar things but in different way/implementation.
PPS. CPAN is so big and wonderful that its easy to miss or even forget whats on there!