6

Sometimes I need a useful utility function, like List::Util::max in the middle of a large program that does lots of stuff. So if I do

use List::Util 'max';

At the top of my program, I'm stuck with that symbol, polluting my whole namespace, even though I only need it in one subroutine.

So I've been thinking of trying a different pattern, instead:

use List::Util ();

# a whole bunch of stuff later...
sub blah { 
    List::Util->import( 'max' );
    $blah = max @foobar;
    ...
}

There are two problems with this, though. For one, it doesn't automatically unimport at the end of the block (drat.) I would have to undo everything with an unimport.

The other problem is that apparently prototypes don't get applied correctly, so I have to say max( @foobar ) instead of the prettier parenthesisless version.

Is there an easy way to temporarily import symbols for a block, which would automagically make them go away at the end of the block, and which would also handle prototypes correctly?

friedo
  • 65,762
  • 16
  • 114
  • 184

4 Answers4

4

Just do this, it's much better and cleaner:

package Foo;
use strict; use warnings;
use List::Util 'max';
use namespace::autoclean;

# your method definitions here...

namespace::autoclean will "unimport" the symbol after the package's compilation cycle is done. The call to it in your method will still work, but you have no namespace pollution (the *Foo::max symbol is removed) and calling $obj->max() will fail.

Alternatively, you might want to take a look at Lexical::Import (I know nothing about it; an irc birdie mentioned it).

Ether
  • 53,118
  • 13
  • 86
  • 159
  • While `namespace::autoclean` is spiffy, it doesn't solve the main problem which is that `max` is still there in the whole package instead of limited to a single block, which is what I'm really trying to achieve. I want my utility functions to behave like lexical variables. – friedo Jun 14 '10 at 17:31
  • @friedo: why is it a problem that `max` is available to the entire package? If the concern is namespace pollution, that's what this module takes care of. – Ether Jun 14 '10 at 17:33
  • Maybe I want a *different* `max` somewhere else in the package. – friedo Jun 14 '10 at 17:35
  • @friedo: I don't know anything about your context, but if I ran into that situation, I'd consider splitting my package into two. – Ether Jun 14 '10 at 17:44
  • 1
    True, that is the practical solution. But I'm just trying to see if there is something cooler than practical. :) – friedo Jun 14 '10 at 17:56
  • 1
    Ah well if it's for *coolness*, then let the discussion continue :D – Ether Jun 14 '10 at 18:01
2

If you only use max in one subroutine, I wouldn't import it into the namespace at all. My solution is to

use List::Util;
sub blah {
    print List::Util::max(@list);
}
Mike
  • 654
  • 2
  • 7
  • 22
1

You can localize a symbol table entry:

use List::Util ();

@y = qw(1 3 5 -9 4);

sub max { # return maximum *absolute value* of list
    my $max = abs(shift);
    $max<abs($_) && ($max=$abs($_))  for @_;
    return $max;
}

sub max2 {
    local *max = *List::Util::max;
    return max(@_);
}

print "My max:         ", max(@y), "\n";    # ==> 9
print "List::Util::max ", max2(@y), "\n";   # ==> 5
mob
  • 117,087
  • 18
  • 149
  • 283
  • 3
    This will introduce subtle errors if either `max` has a prototype, since the effect of that is burned in at compile time. In `max2`, the prototype from `main::max` is used, not the one from `List::Util::max`. Helpfully, you should get a warning about the prototype mismatch on assignment. – Eric Strom Jun 14 '10 at 17:18
  • @Eric Strom - Good point, that would suck. Use a `local` subroutine name with caution. – mob Jun 14 '10 at 17:30
1

perlfunc implies that no MODULE should do what you want:

sub blah {
    use List::Util qw(max);
    say max @foobar;
    no List::Util;
}

but that doesn't work -- at least not for List::Util. I believe that it would need to define an unimport method. Even then, I'm not sure if you could have a bare max in your module call different definitions.

Michael Carman
  • 30,628
  • 10
  • 74
  • 122