[% setvar title UNIVERSAL::import() %]

This file is part of the Perl 6 Archive

Note: these documents may be out of date. Do not use as reference!

To see what is currently happening visit http://www.perl6.org/

TITLE

UNIVERSAL::import()

VERSION

  Maintainer: Michael G Schwern <schwern@pobox.com>
  Date: 18 Sep 2000
  Mailing List: perl6-language@perl.org
  Number: 257
  Version: 1
  Status: Developing

ABSTRACT

UNIVERSAL::import() will be a flyweight method to provide very simple semantics to export variables and functions.

DESCRIPTION

Exporter is big and complicated. It is so big it has been split up into Exporter and Exporter::Heavy. Its so complicated that it takes 264 lines to explain it.

Most of the time, you just want to export some variables and methods. This part of Exporter is almost trivial to implement that people often write their own simple import() routines.

UNIVERSAL::import() would take this basic Exporter::import() functionality and implement it in a very lean fashion. It would support only three of Exporter::import's features.

    @EXPORT      - A default list of symbols to export
    @EXPORT_OK   - A list of symbols to export upon request
    @EXPORT_FAIL - A list of symbols which cannot be exported

Tags, "/pattern/", negation, export_to_level(), export_fail() and all supporting paraphernalia are all left to Exporter. Module version checking is left to UNIVERSAL::require.

Removal of the more obscure features should make basic exporting faster and easier to document. So too would it be made much more efficient if implemented in universal.c.

UNIVERSAL::import() is only a class method.

Because it is strongly recommended that you not export methods, import() makes little sense as an object method. If you have an object for a module, more than likely you're not going to be exporting functions. $obj-import()> should be an explicit run-time error.

This protects against the programming mistake where $class is accidentally an object. $class-import> would silently work, making the mistake difficult to find. This case should be far more common than that of desiring to import a module based on an object.

if import() is desired as an object method, it can be easily overridden with:

    sub import {
        my $proto = shift;
        my $class = ref $proto || $proto;
        return $class->SUPER::import(@_);
    }

Ta da.

MIGRATION

Replace UNIVERSAL::import() with a method that dies with a q|Can't locate object method "%s" via package "%s"| so that Class->import() continues to fail if Class does not implement import() or subclass Exporter.

Class->can('import') remains a problem. UNIVERSAL::can may have to be coded with a special case to cause a failure in Perl5 code.

IMPLEMENTATION

A complete Perl 5 implementation follows:

    package UNIVERSAL;
    
    sub import {
        my($exporter, @imports)  = @_;
        my($caller, $file, $line) = caller;
    
        # XXX Can't tell difference between "use Module" and "use Module ()"
        unless( @imports ) {        # Default import.
            @imports = @{$exporter.'::EXPORT'};
        }
        else {
            if( @{$exporter.'::EXPORT_FAIL'} ) {
                # This can be cached.
                my %fail = map { $_ => 1 } @{$exporter.'::EXPORT_FAIL'};
    
                my($denied) = grep {$fail{$_}} @imports;
                _not_exported($denied, $exporter, $file, $line) if $denied;
            }
    
            # Because @EXPORT_OK = () would indicate that nothing is
            # to be exported, we cannot simply check the length of @EXPORT_OK.
            # We must to oddness to see if the variable exists at all as
            # well as avoid autovivification.
            # XXX idea stolen from base.pm, this might be all unnecessary
            my $eokglob;
            if( $eokglob = ${$exporter.'::'}{EXPORT_OK} and *$eokglob{ARRAY} ) {
                if( @{$exporter.'::EXPORT_OK'} ) {
                    # This can also be cached.
                    my %ok = map { $_ => 1} @{$exporter.'::EXPORT_OK'};
    
                    my($denied) = grep {!$ok{$_}} @imports;
                    _not_exported($denied, $exporter, $file, $line) if $denied;
                }
                else {      # We don't export anything.
                    _not_exported($imports[0], $exporter, $file, $line);
                }
            }
           
        }
    
        _export($caller, $exporter, @imports);
    }
    
    sub _export {
        my($caller, $exporter, @imports) = @_;
    
        # Stole this from Exporter::Heavy.  I'm sure it can be written better
        # but I'm lazy at the moment.
        foreach $sym (@imports) {
            # shortcut for the common case of no type character
            (*{"${caller}::$sym"} = \&{"${exporter}::$sym"}, next)
                unless $sym =~ s/^(\W)//;
            my $type = $1;
            *{"${caller}::$sym"} =
                $type eq '&' ? \&{"${exporter}::$sym"} :
                $type eq '$' ? \${"${exporter}::$sym"} :
                $type eq '@' ? \@{"${exporter}::$sym"} :
                $type eq '%' ? \%{"${exporter}::$sym"} :
                $type eq '*' ?  *{"${exporter}::$sym"} :
                do { require Carp; Carp::croak("Can't export symbol: $type$sym") };
        }
    }
    
    sub _not_exported {
        my($thing, $exporter, $file, $line) = @_;
        die sprintf qq|"%s" is not exported by the %s module at %s line %d\n|,
            $thing, $exporter, $file, $line;
    }
    
    1;

Of course, the efficiency of the implementation can be tightened up. Its just a prototype.

REFERENCES

RFC 233 - Replace Exporter with a better scaling mechanism.

RFC 253 - UNIVERSAL::require()

Exporter