|
Item Description: Sane source filtering for the rest of us
Review Synopsis:
How many of you have written code that looks something like:
package Some::Class;
use strict;
use warnings;
sub new {
my $class = shift;
return bless { @_ }, $class;
}
sub foo {
my $self = shift;
$self->{foo} = shift if @_;
return $self->{foo};
}
sub bar {
my $self = shift;
$self->{bar} = shift if @_;
return $self->{bar};
}
sub baz {
my $self = shift;
$self->{baz} = shift if @_;
return $self->{baz};
}
sub do_something_useful {
...
}
Come on, raise your hands. I know I've done this at least a hundred times. Then, I learned about closures and went back and rewrote that code to look something like:
package Some::Class;
use strict;
use warnings;
sub new {
my $class = shift;
return bless { @_ }, $class;
}
foreach my $name ( qw( foo bar baz ) ) {
no strict 'refs';
*{ __PACKAGE__ . "::$name" } = sub {
my $self = shift;
$self->{$name} = shift if @_;
return $self->{$name};
};
}
sub do_something_useful {
...
}
Now, instead of 98% of the Perl community being able to maintain my code, I'm down to 0.98%. Several managers I've worked for had made me take out code like that, and for good reason. Just because they hired a Perl expert to write the code doesn't mean that they'll be able to hire someone like that to maintain the code. So, it's back to repetition, right?
<Trumpets sound in the distance /> Module::Compile::TT to the rescue! That code using typeglobs and closures now looks like:
package Some::Class;
use strict;
use warnings;
sub new {
my $class = shift;
return bless { @_ }, $class;
}
use tt;
[% FOREACH name IN [ 'foo', 'bar', 'baz' ] %]
sub [% name %] {
my $self = shift;
$self->{[% name %]} = shift if @_;
return $self->{[% name %]};
}
[% END %]
no tt;
sub do_something_useful {
...
}
Whoa! That actually looks readable! Everyone knows how to read TT directives (or they're close enough to your favorite templating module as to be no difference).
But, isn't this a source filter? Well, technically, it is. But, there's a major difference between this and Filter::Simple. Module::Compile::TT compiles this once and installs a .pmc file that you can look at and edit. Or, you could just run TT against this module and see what would happen.
Contrast that to Filter::Simple that won't generates potentially anything and you have no (sane) way of finding out what happened.
The real dealbreaker for me is that I feel pretty sure I could take this to any manager I use to work for and they would all be comfortable with that kind of code in their production codebases. This is code that can be maintained by the masses.
Re: Module::Compile::TT by nothingmuch (Priest) on Jun 17, 2006 at 20:41 UTC |
Note that Module::Compile has many goodies:
- It filters into a .pmc file, so the filter doesn't have to be run every load
- It will regenerate the code when needed (when the original has changed)
- It allows you to ship the "compiled" .pmc instead or in addition to the .pm, so that your code doesn't need to depend on the actual filter tool to work
ingy++! audreyt++!
| [reply] |
Re: Module::Compile::TT by merlyn (Sage) on Jun 17, 2006 at 21:06 UTC |
This would have made a lot of sense as an Inline module, which already has support for parameter passing, caching, compiling, interfacing, inlining, etc.
| [reply] |
|
| [reply] |
Re: Module::Compile::TT by Arunbear (Vicar) on Jun 17, 2006 at 23:21 UTC |
When I read through your example, Class::Accessor immediately came to mind, so I'm not sure what benefit a source filtering approach brings to this problem. There doesn't seem to be much documentation for Module::Compile::TT, so how else can be used e.g. can you use it to simulate macros?
| [reply] |
|
Accessors are just a clear example.
Nowadays I use Moose for them, or if I can't then Class::Accessor.
I needed tt for a set of methods that operate on two different classes in almost the same way. It's still just boilerplate code (the object that needed this code gen was a factory object with some convenience routines).
As for macros - C preprocessor style ones, that is - you can use TT macros. See Template::Manual::Directives.
| [reply] |
Re: Module::Compile::TT by tinita (Parson) on Jun 18, 2006 at 13:07 UTC |
[% FOREACH name IN [ 'foo', 'bar', 'baz' ] %]
sub [% name %] {
my $self = shift;
$self->{[% name %]} = shift if @_;
return $self->{[% name %]};
}
[% END %]
well, to be honest, I don't find that more readable than creating the subs
with writing them directly into the symbol table. I find it less readable.
I see no comments in your original code. With some short comment an average
perl programmer should be able to understand what the code is doing and how to maintain it.
If somebody doesn't understand it, I guess they wouldn't understand the TT-example, either (they'd have to understand how a source filter works). I think
it's better to teach them what an anonymous sub is than how to use inline-TT-code.
And, additionally, not everybody knows or likes TT syntax.
| [reply] [d/l] |
Re: Module::Compile::TT by schwern (Scribe) on Sep 17, 2007 at 04:05 UTC |
Maybe I'm being old fashioned, but didn't we invent subroutines to hide complexity? | [reply] |
Back to Reviews
|