I've been thinking about improvements that could be made to Exporter for
Perl 6.

        1. Choosing where to export to:
                use Data::Dumper 'Dumper' => 'dumpvar';
                #exports Data::Dumper::Dumper to Main::dumpvar (or whatever)
        2. Built-in switch handling:
                use Data::Dumper ('Dumper' : qw(+Indent=2 +Useqq));
                #imports Dumper, sets $Data::Dumper::Indent=2, and
                # does $Data::Dumper::Useqq is true
        3. Warnings about conflicts:
                use warnings 'Exporter';
                sub Dumper { ... }
                use Data::Dumper qw(Dumper);
                #prints out a warning (not just 'redefined subroutine', either).
        4. For modules, saying 'use Exporter' should be enough to get import.
           If you don't want Exporter's import(), just C<use Exporter()>.
        5. In addition to @EXPORT/@EXPORT_OK/%EXPORT_TAGS, provide a way to do
           it on the 'use Exporter' line.

So, with those requirements and what knowledge of Perl 6 I have, I
present a preliminary Exporter.  I typed it directly into the mailer, so
there's a good chance of mistakes.  If anyone spots any mistakes in it,
let me know.


module Exporter;

use strict;
use warnings;
use warnings::register; #assuming this continues to exist...

sub myimport($exp_from : *@symbols=() : *@options=()) {
        my $exp_to=caller.package;      #is this how caller is used?

        pkg_alias($exp_from, "Exporter::From");
        pkg_alias($exp_to, "Exporter::To");

        #defaults
        @symbols //= @Exporter::From::EXPORT;

        #expand tags to their values
        @symbols=map {
                /^:/ ??
                        @{%Exporter::From::EXPORT_TAGS}{$_}} ::
                        $_
        } @symbols;

        for(@symbols) {
                #handle version numbers
                if(/^[\d.]+$/) {
                        $exp_from.require_version($_);
                        next;
                }

                #handle renamed exports
                my($to, $from)=$_.ref eq 'PAIR' ? ($_.left, $_.right) : ($_, $_);

                for($to, $from) {
                        #make sure it has some sort of sigil
                        $_='&' _ $_ unless m'^[$@%&]';
                }

                warnings::warnif("$from exported from $exp_from conflicts with
existing $to at $(caller.file) line $(caller.line).\n")
                        if defined %Exporter::To::{$to};

                die("$exp_from doesn't export $from at $(caller.file) line
$(caller.line).\n")
                        unless grep {/^$from$/} @Exporter::From::EXPORT,
@Exporter::From::EXPORT_OK;

                %Exporter::To::{$to}=%Exporter::From::{$from};
        }

        for(@options) {
                my($sign, $name, $value)=/[+-]([[\w]&&[^\d]]\w+)(?:=(.*))?/s;
                my $targ := ${$Exporter::From::{$name}};

                given($sign) {
                        when('+'): {
                                if(defined $value) {
                                        $targ=$value;
                                }
                                else {
                                        $targ is true;
                                }
                        }

                        when('-') {
                                if($targ.props{true}) {
                                        $targ is false;
                                }
                                else {
                                        undef $targ;
                                }
                        }
                }
        }
}

sub import($pkg : ARRAY $export=undef, ARRAY $ok=undef, HASH
$tags=undef) {
        my $from_pkg=caller.package;

        pkg_alias($from_pkg, "Exporter::From");

        &Exporter::From::import := &myimport;

        #another way to decide what to export:
        #use Exporter(
        #       export  => [qw(foo bar)],
        #       ok              => [qw(baz quux],
        #       tags            => { :argc => ['argv'] }
        #);

        if(defined $export) {
                @Exporter::From::EXPORT=@$export;
        }

        if(defined $ok) {
                @Exporter::From::EXPORT_OK=@$ok;
        }

        if(defined $tags) {
                %Exporter::From::EXPORT_TAGS=%$tags;
        }
}

sub pkg_alias($original, $new) {
        #Any sufficiently encapsulated hack...
        no strict 'refs';

        %{"${original}::"} := %{"${new}::"};    #XXX will this actually work?
}


=head1 NAME

Exporter - Implements default import method for modules

=head1 SYNOPSIS

In MyModule.pm:

        module MyModule;

        use Exporter(
                export => [qw(foo @bar)],
                ok => [qw($baz quux)],
                tags => { :argc => ['@argv %argv']}
        );

In other files:

        use MyModule;           #imports everything in export

        use MyModule 'quux';    #imports only &quux

        use MyModule ':argc'    #imports @argv and %argv

        use MyModule();         #imports nothing at all

=head1 DESCRIPTION

TODO: Fill this in.

=head1 BUGS

Who knows--this code is for a language that hasn't been fully defined
and which doesn't yet have an implementation.

=head1 AUTHOR

The Perl 6 core developers.  (Preliminary version by Brent Dax.)

=cut


--Brent Dax
[EMAIL PROTECTED]
Configure pumpking for Perl 6

"Nothing important happened today."
    --George III of England's diary entry for 4-Jul-1776

Reply via email to