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

In a class I'm coding, I have a method that calls a code ref to process an argument, another to initialize that code ref, and two (e.g.) other methods that handle the processing. Stripped down, the example looks like this:
sub foo_a { my ($self, $arg) = @_; # method A for processing $arg } sub foo_b { my ($self, $arg) = @_; # method B for processing $arg } sub foo { my ($self, $arg) = @_; $self->{'foo_code'}->($self, $arg); # XXX } sub foo_set { my ($self, $code) = @_; $self->{'foo_code'} = $code; }
The user has access to the specific foo_a() and foo_b() methods, or can use foo() to call whatever's registered as the current underlying method.

The line marked "XXX" above bugs me. Having to simulate an object-call to the underlying method by explicitly repeating $self in the calling sequence feels ugly. Is there a better way that this can be done more "directly?"

I had some success changing foo_set() to fiddle with typeglobs as a way of "aliasing" foo() to the underlying method, but this gets in the way of letting users supply their own processing functions via an anonymous code ref:
$obj->foo_set(sub { # process $_[1] });
Is there any magic I'm overlooking that cleans this up?

Replies are listed 'Best First'.
Re: cleaning up an indirect
by chromatic (Archbishop) on Aug 28, 2000 at 20:40 UTC
    Here's a little syntactic sugar to ease the pain:
    sub foo { my ($self, $arg) = @_; &{ $self->{'foo_code'} }; }
    It's still indirect, but as long as you don't shift things out of @_, you don't have to pass $self explicitly. (I can't think of a way to do it otherwise, without actually installing the subroutine in a typeglob in your package somewhere.)
RE: cleaning up an indirect
by johannz (Hermit) on Aug 29, 2000 at 02:20 UTC

    I have some code I'll be posting soon that illustrates this better, but try using the 'can' method. The advantage to this approach is it even passes 'use strict;'

    sub foo_set { my ($self, $code) = @_; my $code_ref = $self->can($code); if (defined($code_ref)) { $self->{'foo_code_ref'} = $code_ref; } else { die "Unable to handle '$code'"; } } sub foo { my ($self, $arg) = _; my $code_ref = $self->{'foo_code_ref'}; $self->code_ref($arg); }

    The 'can' method determines if the class can use that method and returns a code reference. You can then just call the code reference on the object. And like I said, it will pass 'use strict'. With error checking and Carp::croak, you can make it even telling you where in the calling code you made a type.

    This works in at least perl 5.005_03 and I believe also in perl 5.004. I don't know about older version of perl.

    This takes advantage of the 'can' method in the UNIVERSAL package which every package inherits from. If you override the 'can' function in your inheritance tree, this won't work. But that should rarely happen.

RE (tilly) 1: cleaning up an indirect
by tilly (Archbishop) on Aug 28, 2000 at 20:36 UTC
    Do your users need to call foo_a and foo_b themselves? If not then you could use for your methods the same anon code interface that you expect your users to use to create their own handlers.

    If they do, then you can make foo_a and foo_b themselves be wrappers around the same anon code interface.

    I would recommend saying that they don't. Instead let them set, and call the default, and give them access to useful formatting constructors. If they want more constructors, they can build their own and use your code as a direct example.

Re: cleaning up an indirect
by merlyn (Sage) on Aug 28, 2000 at 20:17 UTC
    None that I can think of, and I'm pretty hip on coderefs.

    Seems like you're making singleton objects; you might look into hiding everything behind Class::Classless to pretty-up the interface. Then you can actually make a method call, and the right $AUTOLOAD does the right thing. Maybe that's what you needed.

    -- Randal L. Schwartz, Perl hacker