tags:

views:

434

answers:

5

The question "How can I monkey-patch an instance method in Perl?" got me thinking. Can I dynamically redefine Perl methods? Say I have a class like this one:

package MyClass;
sub new {
  my $class = shift;
  my $val = shift;
  my $self = { val=> $val};

  bless($self, $class);
  return $self;
};

sub get_val {
  my $self = shift;
  return $self->{val}+10;
}
1;

And let's say that adding two numbers is really expensive.

I'd like to modify the class so that $val+10 is only computed the first time I call the method on that object. Subsequent calls to the method would return a cached value.

I could easily modify the method to include caching, but:

  • I have a bunch of methods like this.
  • I'd rather not dirty up this method.

What I really want to do is specify a list of methods that I know always return the same value for a given instance. I then want to take this list and pass it to a function to add caching support to those methods

Is there an effective way to accomplish this?

Follow up. The code below works, but because use strict doesn't allow references by string I'm not 100% where I want to be.

sub myfn {
  printf("computing\n");
  return 10;
}
sub cache_fn {
  my $fnref = shift;

  my $orig = $fnref;
  my $cacheval;

  return sub {
    if (defined($cacheval)) { return $cacheval; }
    $cacheval = &$orig();
    return $cacheval;
  }
}

*{myfn} = cache_fn(\&myfn);

How do I modify to just do this?:

cache_fn(&myfn);
+3  A: 

You can overwrite methods like get_val from another package like this:

*{MyClass::get_val} = sub { return $some_cached_value };

If you have a list of method names, you could do something like this:

my @methods = qw/ foo bar get_val /;
foreach my $meth ( @methods ) {
    my $method_name = 'MyClass::' . $meth;
    no strict 'refs';
    *{$method_name} = sub { return $some_cached_value };
}

Is that what you imagine?

innaM
use strict; does not allow me to refer to functions by string. Is there a way to get around this safely?
mmccoo
you mean a way that does not use "no strict 'refs'" like in my second example? None that I know of.
innaM
+3  A: 

I've never tried it with methods, but Memoize may be what you're looking for. But be sure to read the caveats.

cjm
It would work with methods, but you might have to use the NORMALIZER option if you want to cache across all instances. If you want to cache per instance, then it's probably easier to just stick the value in the object, e.g. return $self->{foo} if exists $self->{foo}
runrig
That's easier if you're writing the class. The question implies he's trying to dynamically modify a class he didn't write.
cjm
A: 

Not useful in your case but had your class been written in Moose then you can dynamically override methods using its Class::MOP underpinnings....

{
    package MyClass;
    use Moose;

    has 'val' => ( is => 'rw' );

    sub get_val {
        my $self = shift;
        return $self->val + 10;
    }

}

my $A = MyClass->new( val => 100 );
say 'A: before: ', $A->get_val;

$A->meta->remove_method( 'get_val' );
$A->meta->add_method( 'get_val', sub { $_[0]->val } );

say 'A: after: ', $A->get_val;

my $B = MyClass->new( val => 100 );
say 'B: after: ', $B->get_val;

# gives u...
# => A: before: 110
# => A: after: 100
# => B: after: 100

/I3az/

draegtun
+1  A: 

How do I modify to just do this?:

cache_fn(\&myfn);

Well based on your current example you could do something like this....

sub cache_fn2 {
    my $fn_name = shift;
    no strict 'refs';
    no warnings 'redefine';

    my $cache_value = &{ $fn_name };
    *{ $fn_name } = sub { $cache_value };
}

cache_fn2( 'myfn' );

However looking at this example I can't help thinking that you could use Memoize instead?

/I3az/

draegtun
+2  A: 

I write about several different things you might want to do in the "Dynamic Subroutines" chapter of Mastering Perl. Depending on what you are doing, you might want to wrap the subroutine, or redefine it, or subclass, or all sorts of other things.

Perl's a dynamic language, so there is a lot of black magic that you can do. Using it wisely is the trick.

brian d foy
thanks for the link. I read a couple pages online and they've got what I would have needed to answer this.Looks like another book is on my shopping list.
mmccoo