http://www.perlmonks.org?node_id=539299

Corion has asked for the wisdom of the Perl Monks concerning the following question:

I'm looking for a way to eliminate common code in a lot of methods that deals with passing on the call context. Any suggestions on how I could restructure my code to be less repetitive are welcome.

I have a set of methods that have a dual life. When called in void context, they act as mutators, and when called in non-void context, they return a modified copy. As an example (and my use case), consider the methods to move the cursor of a text editor through a document.

The idea is to use them in the following setting:

$cursor->move_right(3); # move cursor three chars to the right $cursor->move_down(); # move cursor one line downwards # and my $left_pos = $cursor->move_left(1); # position one char to the left of the cursor my $start = $cursor->start_of_document();

I have many more such methods (end_of_document, ...) and they all look basically the same:

sub frobnicate_position { my ($self) = @_; my $obj; if (defined wantarray) { $obj = $self->copy(); } else { $obj = $self; } ... calculate new position here, ... set $obj->line() and $obj->column() return $obj };

For every method, the calculation can range from pretty simple to moderately complex, but the calculation code seldom spans more than three lines.

What I'm aiming for is to eliminate or reduce the 8 common lines of setup that every method will have, as the repetition strikes me as not elegant. One idea I have is to use a wrapper around the real meat:

sub positional_mutator(&;$) { my ($worker,$name) = @_; no strict 'refs'; *{$name} = sub { my $self = shift; my $obj; if (defined wantarray) { $obj = $self->copy(); } else { $obj = $self; }; $worker->($obj,@_); } }; positional_mutator { my ($obj,@args) = @_; ... calculate new position here }, 'frobnicate_position';

That way, I'll eliminate these setup lines evaluating the context, at the price of some syntactical weirdness - I'll have to install the method through my wrapper and the name now comes after the code. That isn't completely elegant. I could go through Attribute::Handler, but that strikes me as the nuclear solution to weed removal - it also doesn't play well when the module isn't loaded through use, at least when loading the module by use_ok.