24 days of Perl code from RJBS! Feed

Built in Our Workshop, Delivered in Your Package

Sub::Exporter - 2009-12-01

Exporting

In Perl, we organize our subroutines (and other stuff) into namespaces called packages. This makes it easy to avoid having to think of unique names for functions you write, and it means you don't have thousands of core functions to remember. Traditionally, exports were provided by the Exporter module, included with perl5 since its first release.


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 

 

# You use Exporter in your module...
package Gift::Dispatch;

use Exporter qw(import);
our @EXPORT_OK = qw(wrap label send);
our %EXPORT_TAGS = (all => \@EXPORT_OK);

sub wrap { ... }
sub label { ... }
sub send { ... }

 

Now when someone says use Gift::Dispatch ':all' the three exported routines become available in their package. This is a useful tool, and most Perl programmers interact with Exporter all the time. Unfortunately, it's not very flexible. For example, here's our attempt to use Gift::Dispatch:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 

 

package Gift::Giver;

use Gift::Dispatch ':all'; # for physical presents
use Email::Send; # for online gift codes

for my $gift ( @physical_presents ) {
  send($gift, { carrier => 'UPS' });
}

for my $gift ( @gift_codes ) {
  Email::Send->new->send( email_for($gift) );
}

 

Email::Send exports a send routine by default, which will clobber the one from Gift::Dispatch. This is obviously a contrived example, but this problem happens in real life, too, and generally ends up being more obnoxious to track down. Subroutines installed by Exporter also stick around forever, so when somebody ends up trying to call Gift::Giver->send, forgetting that the right method is give, they get one of your imported routines instead of a "no such method" exception.

Sub::Exporter and Naming

Sub::Exporter (sometimes affectionately referred to as S'Ex) makes it easy to fix this problem. First, we update Gift::Dispatch:


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 

 

# You use Exporter in your module...
package Gift::Dispatch;

use Sub::Exporter -setup => [ qw(wrap label send) ];

sub wrap { ... }
sub label { ... }
sub send { ... }

 

And then we update Gift::Giver to muck around with the names (unrelated code omitted):


1: 
2: 
3: 
4: 
5: 
6: 
7: 

 

package Gift::Giver;

use Gift::Dispatch ':all' => { -prefix => '_', -suffix => '_gift' };

for my $gift ( @physical_presents ) {
  _send_gift($gift, { carrier => 'UPS' });
}

 

Sub::Exporter and Code Customization

We got to muck about with the names under which things are imported. This is only the tip of the iceberg. Sub::Exporter can let you customize not just the names of imported routines, but also other facets of their operation. For example, we might not want to require the "carrier" parameter for our shipment above, if we can provide a default. It's easy to let Sub::Exporter handle an import like this:


1: 
2: 
3: 
4: 
5: 
6: 
7: 

 

package Gift::Giver;

use Gift::Dispatch send => { -as => '_send_gift', carrier => 'UPS' };

for my $gift ( @physical_presents ) {
  _send_gift($gift);
}

 

With arguments like that, we can build routines with built-in defaults, overridden behavior, and all kinds of customization, wrapped up in a simple name that we choose.

Over next few weeks, quite a few of the libraries that will be discussed will either use Sub::Exporter to provide exported functions or will use Sub::Exporter to provide powerful customizable interfaces. To really learn how these work, you should go to the Sub::Exporter::Tutorial, but here's a quick non-trivial sample using the canonical closure example of a "counter":


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 

 

package Counter;
use Sub::Exporter -setup => {
  groups => [ counter => \'_gen_counter' ],
};

sub _gen_counter {
  my ($class, $name, $arg) = @_;

  my $cb = $arg->{callback};
  my @log;

  my %sub;
  $sub{record} = sub {
    my ($value) = @_;
    $cb->($value) if $cb and @_;
    push @log, $value;
  };

  $sub{list} = sub {
    return @log;
  };

  $sub{count} = sub {
    return scalar @log;
  };

  return \%sub;
}

 

Then we put it to use...


1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
10: 
11: 
12: 
13: 
14: 

 

use Counter
  -counter => {
    -suffix => '_receipt',
    callback => sub { print "sending thankyou note for $_[0]\n" },
  },
  -counter => {
    -suffix => '_gift',
  },
;

record_receipt($_) for @presents_we_got;
record_gift($_) for @presents_we_gave;

die "life isn't fair" if count_gift > count_receipt;

 

Sub::Exporter is easier to use than Exporter and in its most trivial configuration provides much more utility. Learning how to make the most of Sub::Exporter can make very complex code generation quite simple and can reduce the amount of code you must write significantly.

See Also