Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I alias an object method to different methods on different objects?

Tags:

perl

More easily explained with an example:

my $o = SpecialEffects->new( "config" => 'a' );
my $p = SpecialEffects->new( "config" => 'b' );

$o->sound(); # aliased to fizz(); same as $o->fizz()
$p->sound(); # aliased to clonk(); same as $p->clonk()

Is it possible to do this in Perl? Perhaps using some typeglob or coderef trickery?

I'm trying to keep the SpecialEffects interface simple. I'd prefer not to start building an object hierarchy. The sound() method is what's exposed, only its behaviour can be configured slightly.

I already know that you can alias with *sound = \&fizz; but that is a global thing as far as I know, and I'd like it encapsulated in the object.

like image 908
glts Avatar asked Feb 20 '23 20:02

glts


2 Answers

The simple, easy, non-wizardry way is to just store a method name in your SpecialEffects object, set it according to whatever you want to happen, and call it from sound().

package SpecialEffects;

sub new {
    my $type = shift;
    my %options = @_;
    my $self = {};
    bless $self, $type;
    if($options{config} eq 'a') {
        $self->{sound_method} = 'fizz';
    } elsif($options{config} eq 'b') {
        $self->{sound_method} = 'clonk';
    }
    return $self;
}

sub sound {
    my $self = shift;
    my $method_name = $self->{sound_method};
    $self->$method_name();
}

sub fizz {
    print "fizz\n";
}

sub clonk {
    print "clonk\n";
}

You can store and use coderefs about as easily as method names, if you want to be more wizardly.

package SpecialEffects;

sub new {
    my $type = shift;
    my %options = @_;
    my $self = {};
    bless $self, $type;
    if($options{config} eq 'a') {
        $self->{sound_code} = $self->can('fizz');
    } elsif($options{config} eq 'b') {
        $self->{sound_code} = $self->can('clonk');
    }
    return $self;
}   

sub sound {
    my $self = shift;
    my $code = $self->{sound_code};
    $self->$code();
}
like image 114
chaos Avatar answered Feb 22 '23 11:02

chaos


AFAIK you can't create per-instance methods without storing references in a hash ($o->{sound}() can be done).

Methods are bound to classes, which correspond to perl packages. So you should alialise *SpecialEffects::sound = &SpecialEffects::fizz. This applies to all objects of one class. Sorry this is not javascript etc, I hate this myself...

(You could do some semi-wizadry and create a package SpecialEffects::clonky on the fly (during runtime), which just includes the sound alias for clonk and subclasses SpecialEffects. Then rebless your reference. This is effectively your unwanted hierarchy, but you don't have to actually make the .pm files

EDIT: I'm not sure how exactly this is done, but it goes along these lines ↓ If it works, this is a semi-elegant solution (it will mess up your name space)

sub whatSoundShallIMake {
  my ($self, $sound) = @_;
  no strict 'refs';
  my $newPackageName = "SpecialEffects::make$sound";
  *{"$newPackageName\::sound"} = &{"SpecialEffects::$sound"}; # make the alias
  @{"$newPackageName\::ISA"} = qw(SpecialEffects); # subclass
  return bless $self, $newPackageName; # rebless
}

say ref $o;
# prints "SpecialEffects"
$o = whatSoundShallIMake($o, "fizz");
say ref $0;
# prints  "SpecialEffects::makefizz"

)

like image 25
amon Avatar answered Feb 22 '23 11:02

amon