Michael G Schwern:
# I've rearranged the proposed features a bit to put the long objections
# at the bottom.
#
# Brent Dax wrote:
# > I've been thinking about improvements that could be made to
# Exporter for
# > Perl 6.
# > 3. Warnings about conflicts:
# >     use warnings 'Exporter';
# >     sub Dumper { ... }
# >     use Data::Dumper qw(Dumper);
# >     #prints out a warning (not just 'redefined subroutine', either).
#
# This would be nice.  In fact, it could be retrofitted onto perl5's
# Exporter.
#
# > 4. For modules, saying 'use Exporter' should be enough to
# get import.
# >    If you don't want Exporter's import(), just C<use Exporter()>.
#
# Very nice.  Exporter::Lite does just that.
#
# > 5. In addition to @EXPORT/@EXPORT_OK/%EXPORT_TAGS, provide
# a way to do
# >    it on the 'use Exporter' line.
#
# Getting rid of the @EXPORT* variables entirely would simplify both the
# interface and the internals.  Exporter could then do a lot more
# caching and pre-calculating than it does now since it doesn't have to
# worry "Did they alter @EXPORT since the last time import() was
# called?"

That may be a good idea.  I'll consider it.  Can anybody think of a
reason against it?

# However, shoving everything onto the use line seems of somewhat
# dubious use.  For starters, how do you say "don't export import()" and
# "export these variables please" at the same time?  You start getting
# into special "dont_export" flags.

Huh?

# Why not just a regular function or method?

I like the idea of just dealing with Exporter in one statement and then
forgetting it even exists.  To me, something like:

        use Exporter;
        our $Exp=new Exporter;
        $Exp.default=qw(foo bar);
        $Exp.ok=qw(baz quux);
        $Exp.tags=(':B' => [qw(bar baz)]);

is little better than:

        use Exporter;
        our @ISA='Exporter';
        our @EXPORT=qw(foo bar);
        our @EXPORT_OK=qw(baz quux);
        our %EXPORT_TAGS=(':B' => [qw(bar baz)]);

A little less typing, perhaps, but still too much for my tastes.

# > 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
#
# The above are both rare and of dubious practice.  But that's not the

I disagree.  The first one is something I thought of because of the p5p
'exporting considered harmful' thread (i.e. importing 'Dumper' to
something more sensible); the second one is something many modules
implement hacks to do.  (Carp and CGI both do it in some way;
Data::Dumper, Test::(More|Simple) and most other modules with
configuration variables could benefit from it.)

# real problem I have with it.
#
# What's the biggest problems with Exporter?
#
# 1) The man page is Big and Confusing to the first time module author.
# 2) Exporter is difficult to extend.
# 3) The internals are twisty and confusing.
#
# And they all stem from one issue:
#
# 4) Exporter tries to do too much already.

I can see abstracting out the options into Exporter::Options (or
something), but I don't think the pairwise thing can do so.  I do think
that we should get rid of '!symbol' and especially '/regex/'; they seem
like an unnecessary complication.

# The two biggest blocks to getting your first modules is figuring out
# Makefile.PL (and thus MakeMaker) and figuring out how to export
# functions.  Both are confusing because the man pages are more
# reference than tutorial.  They have to be references because the
# interfaces are big.  The interfaces are big because they try to
# implement lots of features.  They try to implement every feature
# because they're difficult to extend (ie. if you want to add to
# Exporter you have to rewrite Exporter).  They're difficult to extend
# because it wasn't written to be extensible, there's lots of features,
# and in Exporter's case, it does backflips to try and be lightweight if
# you're only using a subset of its functionality (the Exporter::Heavy
# hack).

Maybe it's just me, but I didn't find Exporter difficult to use.  At the
basic levels most people use it at, it's just:
        -Stuff the default exports into @EXPORT.
        -Stuff anything else that's public into @EXPORT_OK.

Tags aren't much more difficult either.  @EXPORT_FAIL is just weird,
though--I don't know why it's there.

# Its interesting to look at who is using what bits of Exporter.  Here's
# a very rough breakdown (crude scanner attached, all praise forwarded
# to Jeff Friedl).
#
#    1271     .pm files scanned
#     279     using or requiring Exporter
#     178     using anything beyond @EXPORT and @EXPORT_OK
#     125     anything beyond @EXPORT, @EXPORT_OK and %EXPORT_TAGS
#      83     using %EXPORT_TAGS
#     113     having their own import()
#       4     using @EXPORT_FAIL
#
# which shows a few interesting things:
#
# 1) Exporter isn't being used as much as I'd thought.

Lots of OO and 'heavy' or 'backend' modules, I suspect.

# 2) More people use %EXPORT_TAGS than I'd thought.
# 3) A lot more people are finding it necessary to write their own
#    import() routine than I thought.
# 4) Almost nobody is using @EXPORT_FAIL (its just Carp and Exporter).

And Carp is only using it to detect a "verbose" switch.

# So here's the priority list I'd write out for Exporter::NG:
#
# 1) Support functionality equivalent to @EXPORT, @EXPORT_OK and
#    %EXPORT_TAGS and that's it.
# 2) Make the man page start with a quick tutorial.
# 3) Make it extensible.

How would you do this?  My idea is that when you load
Exporter::Whatever, it passes a couple coderefs to Exporter::register
(or something).  Those coderefs are given first whack at the parameter
list.

In other words:

        module Exporter::Options;

        use Exporter();

        Exporter::register(&onimport, &onunimport);

        sub onimport(@args is rw) {
                for(@args) {
                        next unless defined;
                        next unless s/-//;      #stringifies pairs

                        if(/=/) {
                                my($left, $right)=split '=';
                                ${%Exporter::From::{$left}=$right;
                                undef $_;
                        }
                        else {
                                ${%Exporter::From::{$_}} is true;
                                undef $_;
                        }
                }
        }

        sub onunimport(@args is rw) {
                for(@args) {
                        next unless defined;
                        next unless s/-//;

                        ($_)=split /=/;

                        undef ${%Exporter::From::{$_}};
                        undef $_;
                }
        }

# The important thing being that Perl 6's exporter has a small, simple,
# easy to explain interface.  That it *doesn't* try to shove every
# feature into one namespace.  And that its easy for people to write
# their own exporters.
#
# Then I'd look at those 113 modules that found it necessary to override
# Exporter's functionality (I've attached a list) and see what they're
# doing.  Once you've got a handle on that, move to step four:

I've removed the things from your list where the reason was blindingly
obvious (pragma, source filter, interface is based on 'use'...) and
reattached it; that brings it down to 68.  Oddly enough, B::* and O.pm
didn't appear on your list.

Your own Test::Simple and Test::More modules were on the list; unless
I'm mistaken, that's because of 'use Test::More tests => N', which could
be handled by options.  ;^)

# 4) Write seperate extensions based on the above.

Just remember that some things are simply too weird.  For example, I
don't think we should try to accommodate Inline as an Exporter
extension.  :^)

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

"Nothing important happened today."
    --George III of England's diary entry for 4-Jul-1776
Attribute/Types.pm
Parse/RecDescent.pm
AnyData/Format/HTMLtable.pm
AnyData/Format/XML.pm
AnyLoader.pm
CGI/Debug.pm
CPAN/Site.pm
Carp/Assert.pm
Class/Contract.pm
Class/Contract/Production.pm
Class/MethodMaker.pm
Class/Prototyped.pm
Convert/Units/Base.pm
Devel/SearchINC.pm
ExtUtils/AutoInstall.pm
Getargs/Long.pm
Hook/LexWrap.pm
IO/LockedFile.pm
ImportTest.pm
Mail/Audit.pm
Mail/Field.pm
Mail/Mailer.pm
Math/BigFloat.pm
Math/BigInt.pm
Memoize/NDBM_File.pm
Memoize/SDBM_File.pm
Net/LDAP.pm
Net/LDAP/ASN.pm
Net/LDAP/Extra.pm
POE.pm
POE/Kernel.pm
POE/NFA.pm
POE/Preprocessor.pm
POE/Session.pm
Pod/Coverage.pm
Pollute.pm
Pollute/Persistent.pm
RTF/Group.pm
Test/Builder.pm
Test/More.pm                    #no options (?)
Test/Simple.pm                  #no options (?)
YAPE/Regex.pm
uny2k.pm
private.pm
protected.pm
public.pm
CGI.pm                          #speed hack (?)
CGI/Carp.pm
Class/Struct.pm
Env.pm
Fatal.pm
File/stat.pm
FileHandle.pm
Getopt/Long.pm
Math/BigInt.pm
Net/hostent.pm
Net/netent.pm
Net/protoent.pm
Net/servent.pm
User/grent.pm
User/pwent.pm
Debconf/Client/ConfModule.pm
Debian/DebConf/Client/ConfModule.pm
Debhelper/Dh_Getopt.pm
LWP/Debug.pm
LWP/Simple.pm
Mail/Field.pm
Mail/Mailer.pm

Reply via email to