Beefy Boxes and Bandwidth Generously Provided by pair Networks Bob
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Re: closure clarity, please

by vitoco (Pilgrim)
on Nov 24, 2009 at 13:15 UTC ( #809069=note: print w/ replies, xml ) Need Help??


in reply to closure clarity, please

As ikegami said, subs are global:

#!perl use v5.10; use strict; use warnings; my $a = shift; sub f { say "global f"; } sub g { say "global g"; my ($a) = @_; sub f { say "local f ($a)"; # line 16 } f(); } f(); g(shift); f(); __END__ C:\Temp>localsub.pl Variable "$a" will not stay shared at C:\Temp\localsub.pl line 16. Subroutine f redefined at C:\Temp\localsub.pl line 15. Use of uninitialized value $a in concatenation (.) or string at C:\Tem +p\localsub.pl line 16. local f () global g Use of uninitialized value $a in concatenation (.) or string at C:\Tem +p\localsub.pl line 16. local f () Use of uninitialized value $a in concatenation (.) or string at C:\Tem +p\localsub.pl line 16. local f () C:\Temp>localsub.pl AAA BBB Variable "$a" will not stay shared at C:\Temp\localsub.pl line 16. Subroutine f redefined at C:\Temp\localsub.pl line 15. Use of uninitialized value $a in concatenation (.) or string at C:\Tem +p\localsub.pl line 16. local f () global g local f (BBB) local f (BBB)

What is a strange behavior is that variables got stuck with a value, even if it is assigned after it's first use... That does not seem to be a compile time assignment.

Conclusion: always define global subroutines or pass variables as parameters to them if defined locally for clarity.


Comment on Re: closure clarity, please
Select or Download Code
Re^2: closure clarity, please
by JadeNB (Chaplain) on Nov 24, 2009 at 17:02 UTC
    What is a strange behavior is that variables got stuck with a value, even if it is assigned after it's first use... That does not seem to be a compile time assignment.

    What does this mean? The behaviour your code displays is what I'd expect; is there some gotcha that you didn't expect?

    I think that one has to be careful speaking of compile time in this example; there's the compilation of the program, which happens at (well) compile time, and then the compilation (if that's the right word) of the sub declaration, which happens at run time **. When the sub is compiled, $a has a value, and it's this value * that is compiled into the sub.

    * I'm intentionally being a bit sloppy here: as ikegami mentions, closures close over variables, not values; but, as the warning that you quote mentions, the actual variable that is closed over will no longer be accessible at the end of the subroutine invocation, so that there is no further way to change its value.
    ** Wrong; see below.

      Well, without reading the explanation for the warning, I would expect ()(BBB)() or (AAA)(BBB)(AAA) or just ()()() like the case of no parameters.

      It seems that sub f (the second one) compiles in runtime, and internal $a is not asigned the first time it is called, but when called from g, it glues the first value it receives.

      Let me show an improved example:

      #!perl use v5.10; use strict; use warnings; my $a = shift; sub f { say "global f ($a)"; } f(); sub g { my ($a) = @_; say "global g ($a)"; sub f { say "local f ($a)"; # line 17 } f(); } f(); g(shift); say "main1 $a"; $a = shift; say "main2 $a"; f(); g(shift); f(); __END__ C:\Temp>localsub.pl AAA BBB CCC DDD Variable "$a" will not stay shared at C:\Temp\localsub.pl line 17. Subroutine f redefined at C:\Temp\localsub.pl line 16. Use of uninitialized value $a in concatenation (.) or string at C:\Tem +p\localsub.pl line 17. local f () Use of uninitialized value $a in concatenation (.) or string at C:\Tem +p\localsub.pl line 17. local f () global g (BBB) local f (BBB) main1 AAA main2 CCC local f (BBB) global g (DDD) local f (BBB) local f (BBB)

      As you can see, the internal variable is empty (uninitialized) the first two times f is called, but as soon as it is defined, it keeps that value forever. Weird...

        Thanks for the clarification. Indeed, your example shows that my statement about the function being compiled at run time is completely wrong!

        Now that I understand it, I think your example offers a great insight into scoping issues, and the difference between compile- and run-time. Let's trace through what happens (or at least what seems to me to be happening).

        1. The original definition of f is compiled.
        2. The definition of g is compiled.

          When we read the my ( $a ) = @_ declaration, we set aside space for $a, but do not yet assign anything to it. This pre-allocated space will be used only in our first call to g.

          Next, we compile the re-definition of f, triggering both the “Subroutine redefined” and the “Variable "$a" will not stay shared” warnings. At this point, we're still in the compilation phase.

          Notice that f is closing over the pre-allocated copy of $a—that is, the one that will be used in the first call to g. This is the weirdness that you're observing.

          To see that all this is happening at compile time, you can add the code block

          BEGIN { $a = "Hi from compile time" }
          before the re-definition of f and observe the changes at run time.

        3. We finish compilation, and assign 'AAA' to $a on line 6. However, this is the ‘outer’ $a, not the one * in the scope of g, so it has no effect on the now-re-defined f.
        4. We run the call to f on line 11. Since f has already been re-defined, we call the re-defined subroutine. Since $a still hasn't been initialised, we get the “Use of uninitialised value” warning. (I think ikegami might prefer “Use of unitialised variable”. :-) )
        5. Nothing happens at run time as we pass over the definition of g—that was dealt with already at compile time.
        6. We call f again on line 22. Since nothing has happened since the last call to f, we get the same result.
        7. We call g with argument 'BBB'. This 'BBB' gets assigned to the pre-allocated $a, which is the one that f closed over, so f ‘sees’ this new value. The re-definition of f is ignored at run time; that happened at compile time.
        8. We say the existing value of the ‘outer’ $a (it's 'AAA', because we assigned it back on line 6), assign a new value, 'CCC', to the ‘outer’ $a, and say that new value. None of this affects the variable $a that f closed over.
        9. We call f again on line 27. Since the $a that f closed over, as opposed to the ‘outer’ one to which we just assigned, has and forever will have the value 'BBB', that's the value that f uses.
        10. We call g again. As g runs, it allocates a new lexical variable $a that has nothing to do with the one that f closed over. (This behaviour is precisely of what the warning about variables not staying shared is—well—warning you.) As a result, the assignment to this new $a has no effect on f. We again skip over the re-definition of f at run time, since we already took care of it at compile time.
        11. We call f again on line 29. Since nothing has changed with its squirreled-away $a, the result is the same as when we called it on line 27.

        * I should be careful referring to “the one in the scope of g”, since, as we shall see, each run of g gets its own fresh lexical $a. However, “one of the ones in the scope of g” is a mess to read, so the imprecision stays.

Re^2: closure clarity, please
by ikegami (Pope) on Nov 24, 2009 at 18:12 UTC

    Conclusion: always define global subroutines

    You have no choice. Named subroutines are always global. You're lying to yourself when you said "local f".

    What is a strange behavior is that variables got stuck with a value,

    Why do you think that creating a variable somewhere should replace a variable in some unrelated sub?

      You have no choice. Named subroutines are always global. You're lying to yourself when you said "local f".

      You are right. I meant "define all subroutines at the same global level."

      Why do you think that creating a variable somewhere should replace a variable in some unrelated sub?

      It is not clear to me when a lexical variable is used and when is lost during the program execution, as in my last example, where I'd expect an uninitialized $a in all "local f"'s messages.

        Ok, I can understand that confusion. Lexicals are actually allocated very early on (compile-time?) and on scope exit. This allows for the creation of private variables:
        package Foo; my $x; sub set_x { validate($_[0]); $x = $_[0]; } sub get_x { die("uninitialized") if !defined($x); $x } 1;
        or
        { my $x; sub set_x { validate($_[0]); $x = $_[0]; } sub get_x { die("uninitialized") if !defined($x); $x } }

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://809069]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (8)
As of 2014-04-21 15:28 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (495 votes), past polls