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

Module:

package MyModule; use IO::Socket::INET; 1;

Main script:

use MyModule; my $socket = IO::Socket::INET->new();

This works fine, but if I now replace/reimplement MyModule so that it has the same API (intended to be a drop-in replacement) but doesn't need (and thus use) IO::Socket::INET internally, the main script will break because it can't find IO::Socket::INET anymore.

To avoid something like this from happening, I was wondering if it's possible to stop use statements within modules from effectively propagating into their caller. This would mean that the example I posted would break and force me to use IO::Socket::INET right away, preventing issues later on.

(This bleeding actually seems to go both ways. I just had a module break on me because I removed a use of another module from my main script. And in this it was actually one of the modules used by the module I removed that actually used the module needed by the second module.)

Replies are listed 'Best First'.
Re: Preventing used modules from leaking to main script
by jdtoronto (Prior) on Sep 20, 2006 at 15:33 UTC
    The use IO::Socket::INET; should be in the main if main is going to call it!

    Perl only loads a module once, a list of loaded modules is kept and after the first use it is never actually loaded again. BUT, and there is always a BUT, you need to put use statements wherever you need to use the module, because the original may now be out of scope.

    jdtoronto

      The use IO::Socket::INET; should be in the main if main is going to call it!

      And that's exactly what I want to enforce.

      Perhaps I didn't make myself clear enough. What I'm saying is that there is apparently no way right now to make sure that your script/module has a use (or require) statement for every module it's using.

      It's happened to me on multiple occasions that I forgot to add a use statement (most often when adding the very first Data::Dumper debug message in a script), and instead of breaking right away the script would work fine until I removed an unrelated module which did happen to use Data::Dumper.

      (Of course you can very well make the argument that not checking to make sure you've included all needed modules is sloppy coding.)

        And that's exactly what I want to enforce.

        You get that behaviour out of the box:

        { package FOO; use Data::Dumper; print Dumper \"bar"; } print Dumper \"baz"; __END__ Name "main::Dumper" used only once: possible typo at - line 7. $VAR1 = \'bar'; print() on unopened filehandle Dumper at - line 7.

        However, if you call Dumper by its fully qualified name (ie Data::Dumper::Dumper, or IO::Socket::INET->new in your example), there's no way to prevent it from working:

        { package FOO; use Data::Dumper; print Dumper \"bar"; } no Data::Dumper; ## not even this print Data::Dumper::Dumper \"baz"; __END__ $VAR1 = \'bar'; $VAR1 = \'baz';

        --
        David Serrano

        And indeed I would make that very point. You should check, because when an error like that occurs that is how you know you have left something out! You can catch some things, you can save in having to use Data::Dumper; everywhere by creating a debug method in your base class which prints the Dumper method, inherit that in every module and then you never need to use Data::Dumper; again! It also means you can use a global to turn the debug printing on and off. That's what I do, I have a $DEBUG variable at the top of the code, it will turn the dumping on or off.

        But sometimes you just gotta face up to the fact that you are human and deal with it! I do, every day!!

        jdtornto

Re: Preventing used modules from leaking to main script
by ikegami (Patriarch) on Sep 20, 2006 at 15:45 UTC

    use and require load the module. Once the module is loaded, it's available to everyone. There's no propogation, since it's a global effect. Being a global effect, you can't make the module unavailable to main.

    You could break IO::Socket::INET for everyone, though.

    package MyModule; use IO::Socket::INET (); undef \&IO::Socket::INET::configure;

    Update: s/new/configure/ in code block to prevent IO::Socket->new(Domain => AF_INET, ...).

Re: Preventing used modules from leaking to main script (symbolic--)
by tye (Sage) on Sep 20, 2006 at 20:09 UTC

    That's just one of the problems with symbolic references (to packages, in this case). Unfortunately, Perl 5 doesn't really support anything but symbolic references when it comes to packages. It'd be nice if Perl 6 supported "real" references to packages and also supported something like "use strict 'packages'" to cause using symbolic reference to a package to become a fatal error.

    More on this idea can be found at Re: $foo = "Foo::Bar"; $foo->new() works? (factories++).

    In the mean time, you can do your best to avoid symbolic references to packages, mostly by using factories (for example, see Re: Getting rid of "new" (wrong target; perl6)).

    Then you'd never write IO::Socket::INET->new(), you'd write code like this instead:

    require IO::Socket::INET; my $SocketFactory= "IO::Socket::INET"; # ... my $socket= $SocketFactory->new( ... );

    and that last line would fail if you hadn't included that first line in that particular chunk of code.

    Some modules are nice enough to support the use of factories more cleanly such that you only have to type their module name once. But typing the module name twice as I show above is still better than typing it every time you want to create a new instance from it.

    - tye