Skip to content
This repository has been archived by the owner on Mar 30, 2018. It is now read-only.

chocolateboy/mysubs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mysubs

NAME

mysubs - lexical subroutines

SYNOPSIS

package MyPragma;

use base qw(mysubs);

sub import {
    my $class = shift;

    $class->SUPER::import(
         foo   => sub { ... },
         chomp => \&mychomp
    );
}
#!/usr/bin/env perl

{
    use MyPragma;

    foo(...);
    chomp ...;
}

foo(...);  # error: Undefined subroutine &main::foo
chomp ...; # builtin

DESCRIPTION

mysubs is a lexically-scoped pragma that implements lexical subroutines i.e. subroutines whose use is restricted to the lexical scope in which they are imported or declared.

The use mysubs statement takes a list of key/value pairs in which the keys are subroutine names and the values are subroutine references or strings containing the package-qualified names of the subroutines. In addition, mysubs options may be passed.

The following example summarizes the type of keys and values that can be supplied.

{
    use mysubs
        foo      => sub ($) { ... },     # anonymous sub value
        bar      => \&bar,               # code ref value
        chomp    => 'main::mychomp',     # sub name value
        dump     => '+Data::Dump::dump', # load Data::Dump
       'My::foo' => \&foo,               # package-qualified sub name
       -autoload => 1,                   # load modules for all sub name values
       -debug    => 1                    # show diagnostic messages
    ;

    foo(...);                            # OK
    prototype('foo')                     # '$'
    My::foo(...);                        # OK
    bar;                                 # OK
    chomp ...;                           # override builtin
    dump ...;                            # override builtin
}

foo(...);                                # error: Undefined subroutine &main::foo
My::foo(...);                            # error: Undefined subroutine &My::foo
prototype('foo')                         # undef
chomp ...;                               # builtin
dump ...;                                # builtin

OPTIONS

mysubs options are prefixed with a hyphen to distinguish them from subroutine names. The following options are supported:

-autoload

If the value is a package-qualified subroutine name, then the module can be automatically loaded. This can either be done on a per-subroutine basis by prefixing the name with a +, or for all named values by supplying the -autoload option with a true value e.g.

use mysubs
     foo      => 'MyFoo::foo',
     bar      => 'MyBar::bar',
     baz      => 'MyBaz::baz',
    -autoload => 1;

or

use MyFoo;
use MyBaz;

use mysubs
     foo =>  'MyFoo::foo',
     bar => '+MyBar::bar', # autoload MyBar
     baz =>  'MyBaz::baz';

The -autoload option should not be confused with lexical AUTOLOAD subroutines, which are also supported. e.g.

use mysubs AUTOLOAD => sub { ... };

foo(); # OK - AUTOLOAD
bar(); # ditto
baz(); # ditto

-debug

A trace of the module's actions can be enabled or disabled lexically by supplying the -debug option with a true or false value. The trace is printed to STDERR.

e.g.

use mysubs
     foo   => \&foo,
     bar   => sub { ... },
    -debug => 1;

METHODS

import

mysubs::import can be called indirectly via use mysubs or can be overridden by subclasses to create lexically-scoped pragmas that export subroutines whose use is restricted to the calling scope e.g.

package MyPragma;

use base qw(mysubs);

sub import {
    my $class = shift;

    $class->SUPER::import(
         foo   => sub { ... },
         chomp => \&mychomp
    );
}

Client code can then import lexical subs from the module:

#!/usr/bin/env perl

{
    use MyPragma;

    foo(...);
    chomp ...;
}

foo(...);  # error: Undefined subroutine &main::foo
chomp ...; # builtin

The import method is implemented as a wrapper around [import_for](#import_for).

import_for

mysubs methods are installed and uninstalled for a particular client of the mysubs library. Typically, this client is identified by its class name i.e. the first argument passed to the [mysubs::import](#import) method. Note: if mysubs->import is called implicitly (via use mysubs ...) or explicitly, then the client identifier is "mysubs" i.e. mysubs can function as a client of itself.

The import_for method allows an identifier to be specified explicitly without subclassing mysubs e.g.

package MyPragma;

use base qw(Whatever); # we can't/don't want to subclass mysubs

use mysubs (); # don't import anything

sub import {
    my $class = shift;
    $class->SUPER::import(...); # call Whatever::import
    mysubs->import_for($class, foo => sub { ... }, ...);
}

The installed subs can then be uninstalled by passing the same identifier to the [unimport_for](#unimport_for) method.

Note that the import_for identifier has nothing to do with the package the lexical subs will be installed into. Lexical subs are always installed into the package specified in the package-qualified sub name, or the package of the currently-compiling scope.

mysubs->import is implemented as a call to mysubs->import_for i.e.

package MyPragma;

use base qw(mysubs);

sub import {
    my $class = shift;
    $class->SUPER::import(foo => sub { ... });
}

- is equivalent to:

package MyPragma;

use mysubs ();

sub import {
    my $class = shift;
    mysubs->import_for($class, foo => sub { ... });
}

unimport

mysubs::unimport removes the specified lexical subs from the current scope, or all lexical subs if no arguments are supplied.

use mysubs foo => \&foo;

{
    use mysubs
        bar => sub { ... },
        baz => 'Baz::baz';

    foo ...;
    bar(...);
    baz;

    no mysubs qw(foo);

    foo ...;  # error: Undefined subroutine &main::foo

    no mysubs;

    bar(...); # error: Undefined subroutine &main::bar
    baz;      # error: Undefined subroutine &main::baz
}

foo ...; # ok

Unimports are specific to the class supplied in the no statement, so pragmas that subclass mysubs inherit an unimport method that only removes the subs they installed e.g.

{
    use MyPragma qw(foo bar baz);

    use mysubs quux => \&quux;

    foo;
    quux(...);

    no MyPragma qw(foo); # unimports foo
    no MyPragma;         # unimports bar and baz
    no mysubs;           # unimports quux
}

As with the [import](#import) method, unimport is implemented as a wrapper around [unimport_for](#unimport_for).

unimport_for

This method complements the [import_for](#import_for) method. i.e. it allows the identifier for a group of lexical subs to be specified explicitly. The identifier should match the one supplied in the corresponding import_for method e.g.

package MyPragma;

use mysubs ();

sub import {
    my $class = shift;
    mysubs->import_for($class, foo => sub { ... });
}

sub unimport {
    my $class = shift;
    mysubs->unimport_for($class, @_);
}

As with the import_for method, the identifier is used to refer to a group of lexical subs, and has nothing to do with the package from which those subs will be uninstalled. As with the import methods, the unimport methods always operate on (i.e. uninstall lexical subs from) the package in the package-qualified sub name, or the package of the currently-compiling scope.

CAVEATS

Lexical subs cannot be called by symbolic reference e.g.

This works:

use mysubs
    foo      => sub { ... },
    AUTOLOAD => sub { ... }
;

my $foo = \&foo;

foo();    # OK - named
bar();    # OK - AUTOLOAD
$foo->(); # OK - reference

This doesn't work:

use mysubs
    foo      => sub { ... },
    AUTOLOAD => sub { ... }
;

my $foo = 'foo';
my $bar = 'bar';

no strict 'refs';

&{$foo}(); # not foo
&{$bar}(); # not AUTOLOAD

VERSION

1.14

SEE ALSO

AUTHOR

chocolateboy, with thanks to mst (Matt S Trout), phaylon (Robert Sedlacek), and Paul Fenwick for the idea.

COPYRIGHT AND LICENSE

Copyright © 2008-2011 by chocolateboy.

This is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.