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 whichbar()
callsSome::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:
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. :-)
I'm not sure that, strictly speaking, this counts as a closure, though it certainly looks like one.
I think of closures as retaining the values of local variables external to the subroutine even if those values subsequently change. The subroutine in the example does not behave that way -- if you change
$old_foo
to a different code reference, the new code gets called.produces
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
Hm. I would have read the first two sentences of the Google article as requiring retention of values. My standard closure example is
which seems to demonstrate retention of values. Can you elucidate further?
Self-reply. On a bit of reflection, I find that my value retention example above is irrelevant to Aristotle's statement about what a closure is. It just illustrates how I think of them, and how I tend to use them. I do still think the Google article's second sentence explicitly requires value retention, and if Aristotle or anyone would be willing to address that point I would appreciate it.
You seemed to be saying that
is an example of one closure but
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.)
Aristotle: The headline of this post is that you have convinced me that the code I said was not a closure actually is one. Thanks for your persistence.
Here is my current understanding of the Perl implementation of closures:
Correct. If you do something like
then the closures in
$getter[0]
and$setter[0]
will refer to the same instance of$value
.I.e. if you do
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.
Yes. I think the thing that tripped me was the idea that a closure and a non-closure could refer to the same instance of a lexical variable. But they can if you hold your tongue right.
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:
because
some_util_func
closes over$var
, and since named subs are created only once, it retains the initial instance of$var
created whilecomplicated_sub
was being compiled – whereas themy
incomplicated_sub
creates an entirely new instance of$var
for every call ofcomplicated_sub
, and that instance is always(!) different from the instance thatsome_util_func
has closed over.Thanks again. I am comfortable enough with closures for my own uses, but as an autodidact I'm short on theory.
I did not exhibit the code that brought me considerably nearer to your level of understanding. What I did there was have
make_adder_and_setter()
actually assign the subroutines to the stash, and then call them that way outside the subroutine, as well as by the returned references. Calling via the stash got me the most-recently-created subroutine -- which was undoubtedly (at my previous level of understanding) a closure, but which was also effectively a named subroutine.This exchange has been very educational for me, but it is also off-topic to the original blog entry on the
redefine
warning, and deserves to be a top-level blog. Would you be willing to write it and publish under your own name? If you are unable, I am willing to write up this exchange (with credit), but I consider that second-best; most of the information came from your brain, not mine.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.
Random thoughts:
if
for how I handled it when I did the write-up.Random meta-thought: is it time to take this off-line? I'm wyant at cpan dot org, which forwards to harryfmudd at comcast dot net.