Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static local dispatch table with OO calls within closures

Tags:

perl

perl5.8

I have a dispatch table that I wish to initialize only once, and is only intended to be used by one function. I was hoping to move the dispatch table outside of the subroutine and into the same anonymous block, but since the dispatch table uses closures to call methods for the object passed into the function, moving the table outside the function separates it from access to the object. What other options do I have for this dispatch table?

I'm using Perl 5.8, so unfortunately I'm unable to use state variables.

sub foo {
  my ($self, $var) = @_;

  my %funcs = (
    a => sub { $self->_a() },
    b => sub { $self->_b() },
    ...
  );

  return $funcs{$var}->();
}
like image 668
kei-clone Avatar asked Sep 25 '13 15:09

kei-clone


1 Answers

Your functions in the dispatch table are closures over $self. If you pass in the $self as a parameter, you can get around that. Note that state variables are not true closures over $self, and would require an explicit parameter as well.

my %funcs = (
  a => sub { shift->_a },  # these are like anonymous methods
  b => sub { shift->_b },
);

sub foo {
  my ($self, $var) = @_;
  my $meth = $funcs{$var} || die "There is no entry $var";
  return $self->$meth();   # sugary syntax
}

Here is a demonstration why state would be a bad idea:

use 5.010;
package Foo;
sub new { my ($c, $v) = @_; bless \$v, $c }

sub foo {
  my ($self) = @_;
  state $cb = sub { say $$self };
  $cb->();
}

Foo->new($_)->foo for 1..3;

Output:

1
1
1

The inner sub is a closure, but the initialization of $cb is only performed once. Thus the closed over $self is the first one.

like image 54
amon Avatar answered Nov 12 '22 13:11

amon