Question

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}->();
}
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top