pbeckingham has asked for the wisdom of the Perl Monks concerning the following question:

I have asked before ("use" modifiers) about modifying the use statement for a module, as in:

use Test::More tests => 5;
I wondered how it was done. Now I have a working version of this, I would like critique, in order to improve or fix it. Specifically, I want to know if the sub import is good - the sample sub1 is irrelevant. Here is the code:
package sample; our $value = 0; use strict; use Exporter; use vars qw(@ISA @EXPORT); @ISA = qw(Exporter); @EXPORT = qw(sub1); sub import { my @imports; while (my $arg = shift) { if ($arg eq 'value') { $value = shift; next; } push @imports, $arg; } sample->export_to_level (1, @imports); } sub sub1 () {$value + 1} 1;
And now I can use this module and override $value if I wish:
#! /usr/bin/perl -w use strict; use sample value => 5; print sub1() , "\n";
Thank you.

Replies are listed 'Best First'.
Re: "use" modifier code via import()
by tilly (Archbishop) on May 17, 2004 at 22:32 UTC
    First of all I should note that Exporter offers export_fail which can be used for the same purpose. You can glance at the implementation of Carp to see how you might use it.

    In the past (particularly with Perl 5.6) I've noticed that export_to_level does not work exactly like you would want. (It reports errors at the wrong level.) Therefore this alternate strategy may be more reliable.

    I also notice that you are using prototypes in your definition of sub1. There was an excellent article on perl.com explaining prototypes in Perl and demonstrating why they are problematic. Unfortunately the link to it isn't working very well at the moment, so I can't direct you to read that article. But I can assure you that if your understanding of prototypes are shaped by prototypes in any other language, that Perl's version of the same is pretty definitely not what you want, and you're better off forgetting that you ever heard about them.

    I also have to second perrin's comment. While playing around with export semantics can achieve all sorts of cool effects, unless it is really important for what your module does, it is better programming practice to stick to normal export semantics and not overload your export semantics.

      First of all I should note that Exporter offers export_fail which can be used for the same purpose.
      Yes, but while I am currently overriding import and removing the two items from the list, in this case I would override the export_fail method and remove two items from the list there. As I see it, both implementations are similar, but I will take a look at Carp.
      (It reports errors at the wrong level.) Therefore this alternate strategy may be more reliable.
      Agreed. That makes for better diagnostics.
      There was an excellent article on perl.com explaining prototypes in Perl and demonstrating why they are problematic. ...
      Oh no! Has the camel misled me? Are prototypes not what I thought? I do enjoy the diagnostics on erroneously constructed sub calls, but is there a downside? I couldn't find an article on perl.com about this. Is there a PM article containing same? I couldn't locate one.
      I also have to second perrin's comment. While playing around with export semantics can achieve all sorts of cool effects, unless it is really important for what your module does, it is better programming practice to stick to normal export semantics and not overload your export semantics.
      I wouldn't say it's really important for the module, just a stylistic preference, with some notion of clean-looking client code.

      Thank you. I appreciate the considered response.

        As far as I know, no perlmonks article exists covering the problems with prototypes for the simple reason that people always point to Christensen's. But it has been discussed, see When to use Prototypes? or Are prototypes evil?.

        As for whether the Camel mislead you, probably not. You probably mislead yourself. Prototypes do exactly what the Camel tells you that they do. Whether you noticed the consequences of the same is another story.

Re: "use" modifier code via import()
by perrin (Chancellor) on May 17, 2004 at 21:06 UTC
    Is it good? In my opinion, no. Doing this kind of trick with import subs just leads to code that is more confusing and harder to maintain. Why not separate the use() from the other subroutine call? They really have no reason to be together.

      I was intending to combine the use with the value override because I like the way that Test::More does this. In my opinion, it is a good place to set a one-time value, without polluting the namespace. It keeps the code compact from the user's perspective.

      Regarding the import trick, I will ensure it is clearly- and well-documented. I didn't think of it as a trick, because I had seen it before in standard Perl distribution code, and considered it tacitly accepted by the community at a minimum, and familiar at least to those who write unit tests.

      Thank you for responding. I respect your opinion.

        Setting a global variable from another module is also another dangerous practice. Sometimes it is useful, witness Test::More. However it is a fragility in the code. For instance if there is any possibility that your module can be used twice in the same code, with different defaults, you have no sane behaviour.

        Therefore I'd advise you to think very carefully before creating APIs that set some global value, let alone trying to make it easy in your API to do that. There are times when it is clearly so unlikely to cause problems that nobody could reasonably object. But those occasions are few and far between. Test::More should be viewed as far more of the exception than the rule in this respect.

        It's easy to set a one-time value without polluting the namespace:
        use Test::More; Test::More::plan( tests => 3 );
        This code does the same thing, but has no tricks that will make someone trying to maintain it scratch their head. To each their own, but I wouldn't want anyone using sneaky import() tricks in code that I would be responsible for maintaining some day.
Re: "use" modifier code via import()
by Solo (Deacon) on May 18, 2004 at 13:30 UTC
    I would point out that sub import is called during syntax checks as well, not that this is a problem in your example, but something to watch out for. (Often, people forget this detail...)

    In pmtest.pm:

    package pmtest; sub import { print "sub import called\n"; } 1;

    In pmtest.pl:

    use pmtest;

    Result:

    >perl -c pmtest.pl sub import called pmtest.pl syntax OK
    --Solo
    --
    You said you wanted to be around when I made a mistake; well, this could be it, sweetheart.
Re: "use" modifier code via import()
by TomDLux (Vicar) on May 17, 2004 at 20:59 UTC
    should have read instead of replying

    --
    TTTATCGGTCGTTATATAGATGTTTGCA