develooper Front page | perl.perl6.language | Postings from December 2001

Re: Perl 6's Exporter

Thread Previous | Thread Next
From:
Michael G Schwern
Date:
December 22, 2001 22:58
Subject:
Re: Perl 6's Exporter
Message ID:
20011223065832.GW672@blackrider
On Sat, Dec 22, 2001 at 09:20:08PM -0800, Brent Dax wrote:
> # 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?

If you have to do this:

    use Exporter (
        export => ...
    );

how do you also do this:

    use Exporter ();

you're making Exporter::import do double-duty.

As an example of this thing being troublesome, Test::More's interface
was originally just this:

    use Test::More tests => 42;

but it also exports a whole bunch of functions.  Somebody wanted to
control what functions it exports.  Normally it would be something
like:

    use Test::More qw(is ok);

but then how do you say how many tests you're going to run?  You
don't.  I had to add in 'import' key:

    use Test::More tests => 42, import => [qw(is ok)];

if I had it to do over again, I would have done the 'use' and the
'tests' as seperate statements.


> # 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.  

"Sure you can do it in one line.  Is it ok if that line is 240
characters long?"  

> 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.

I was thinking something more like:

    use Exporter;
    Exporter.export(qw(foo bar));
    Exporter.ok(qw(baz quux));
    Exporter.tags( B => [qw(bar baz)] );

or perhaps something in between:

    use Exporter;
    Exporter.export(
        export  => [qw(foo bar)],
        ok      => [qw(baz quux)],
        tags    => { B => [qw(bar baz)]
    );

which is very close to yours:

    use Exporter (
        export => [qw(foo bar)],
        ok     => [qw(baz quux)],
        tags   => { B => [qw(bar baz)]
    );

only it leaves Exporter's import() routine open to handle Exporter's
own exports.


> # > 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.)

I can agree with that enough to not argue about 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.  

Pairwise thing?


> I do think that we should get rid of '!symbol' and especially
> '/regex/'; they seem like an unnecessary complication.

Yeah, chuck 'em.  It would be interesting to see how often that's
getting used.


> # 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.

Yes, it is that easy, though tags get a little weird because they
don't automaticly populate @EXPORT_OK or something.  That could be
made easier.

But it comes off as really complicated.  Part of the problem is the
"@ISA = qw(Exporter)" which initiates an "I don't understand OO"
reaction.  So that's going away.  Part of it is how Exporter's
manual is structured.  Part of it is the size.

The bigger the interface, the hard it is to explain.


> # 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.

I think you're thinking of something waaaay more complicated than I
am.  Sounds like you want something that can add new functionality to
Exporter.  I was thinking just make Exporter easy to subclass.


> # 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.

B::* don't override import(), they use O for that.  I missed O because
it isn't in the directories I scanned.  Debian scatters the perl
installation all over the place.


> 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.  ;^)

Yeah, it was a good idea at the time.  Don't fall into your own trap.


> # 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.  :^)

Look for commonalities.


-- 

Michael G. Schwern   <schwern@pobox.com>    http://www.pobox.com/~schwern/
Perl Quality Assurance	    <perl-qa@perl.org>	       Kwalitee Is Job One
Your words scratch the back of my eyes!
	http://sluggy.com/d/010204.html

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About