My Favorite Warnings: redefine

Sooner or later any programmer, writing in any language, will run across something like this Perl warning: Subroutine foo redefined. This is telling you that somehow, somewhere, you tried to create two subroutines with the same name.

Tracking this down can be tricky, and remediation equally so. The possibilities include:

  • If one of your own subroutines is involved, you can rename it.
  • If an imported subroutine that you do not need is involved, you can just not import it. This, of course, involves figuring out which imports you do need. The use of default import lists can make tracking this one down tricky.
  • You can use Importer to import a subroutine under a different name.
  • You can ignore the import mechanism and populate the symbol table yourself with something like BEGIN { require Some::Module; *foo = \&Some::Module::bar; }, after which bar() calls Some::Module::foo().

But maybe you really want to replace a subroutine, in which case you do not want the warning. For example you might want to tweak the action of a subroutine with a hot patch. A trivial example looks something like this:

{
    no warnings qw{ redefine };
    my $old_foo = \&foo;
    *foo = sub {
        warn "Debug - Called foo( qw{ @_ } )";
	goto $old_foo;
    };
}

This disables the redefine warning. saves the code reference to foo() and replaces it with code that logs the arguments (sort of) and then transfers control to the original foo(). The block is to restrict the scope of the no warnings pragma, but it could equally be a BEGIN block if your use case requires it.

Previous entries in this series:

  1. A Belated Introduction
  2. once
  3. redundant and missing
  4. exiting
  5. uninitialized

15 Comments

I never think about this ! if keep a reference of a subroutine, even changing the subroutine that the name refer to, it still keep exist. this is suprise.

I use that closure idiom, particularly when mocking things when writing unit tests. It comes in handy.

That's actually an interestging question - if you did this in a closure, does the subroutine if there are no longer any references to it get GC'd or do they remain somewhere in memory? You seem to be suggesting they do. I don't know the answer, but would like to. :-)

Having to retain the value at the time the closure was created is not a requirement I have ever seen in any definition of closures. (If that was required by the definition, then Perl would not have closures, period, because a variable name in Perl always binds to a reference, not a value.) In fact every example I can back to emphasises the fact that the value of a closed-over variable can be updated and will then be seen by all of the functions that have closed over that particular instance of the variable.

Pretty much every every single Google result shows the same definition: https://www.google.com/search?q=define+Closure+programming

You seemed to be saying that

sub make_adder {
    my ( $addend ) = @_;
    return sub { $_[0] + $addend };
}

is an example of one closure but

sub make_adder_and_setter {
    my ( $addend ) = @_;
    return (
        sub { $_[0] + $addend },
        sub { $addend = $_[0] },
    );
}

is an example with zero closures rather than (correctly) two closures, merely because the second closure mutates the variable. (If that is not what you were saying, I am not sure what “even if those values subsequently change” is supposed to have meant.)

Correct. If you do something like

my ( @getter, @setter );
for my $i ( 'a' .. 'z' ) {
    my $value = $i;
    push @getter, sub { $value };
    push @setter, sub { $value = $_[0] };
}

then the closures in $getter[0] and $setter[0] will refer to the same instance of $value.

I.e. if you do

$setter[0]->( $something );
say $getter[0]->();

you will get the $something printed that you just set.

But while $getter[0] and $setter[0] refer to the same instance of $value, it’s a different instance than the one $getter[1] and $setter[1] (or any other pair) both refer to.

Because of the my declaration, a new instance of $value is created on every iteration of the loop, therefore subs created during different iterations of the loop refer to different instances of the variable. But for the same reason, all subs created during the same iteration of the loop refer to the same instance.

And the fact that the variable is declared outside those subs but they retain a reference to a particular instance of it is indeed what makes those subs closures. The value of the variable may well change, it’s the retention of a particular instance that counts.

Ah, I didn’t properly register that you were talking about named subs. Yes, named subs aren’t special in any way in this regard. The only thing that distinguishes them from unnamed subs is that they get assigned to a glob slot in the package stash, which isn’t relevant in closure terms. They also get created very early on in the execution of the program, which is much more relevant – though not a defining feature. Named subs will in fact be closures if they refer to my variables declared e.g. at the top scope of the file as (faux!) globals.

This is, btw – since we’re in a series on warnings! – what the “Variable "…" will not stay shared” warning is all about. If you define a named sub inside another sub, well – named subs are created just once, immediately when they are parsed, and if the (seemingly) inner, named sub closes over a variable declared in the outer sub, then it will retain a reference to the instance of the variable from during compilation, whereas each call of the outer sub will create new instances of these variables. So something like this cannot work:

sub complicated_sub {
  # ...
  my $var;
  sub some_util_func { $var = some_long_expression @_ }
  # ...
  some_util_func( $something );
  # oops, the $var we can access here has not changed!
  # ...
}

because some_util_func closes over $var, and since named subs are created only once, it retains the initial instance of $var created while complicated_sub was being compiled – whereas the my in complicated_sub creates an entirely new instance of $var for every call of complicated_sub, and that instance is always(!) different from the instance that some_util_func has closed over.

I am willing to write it up, no problem. The problem I do have is I don’t know quite what the opening of such an article would look like. Here in this comment section, the choice of facts to outline was motivated by your statements. I’m not sure how to divorce the explanation from that context; setting it up by conveying your previous understanding of closures in order to launch into its correction doesn’t seem a deft way to bring the reader along. I need some hook for the explanation.

Leave a comment

About Tom Wyant

user-pic Fine Perl code for over 0.005 centuries.