Outer scope of a sub inside a sub 
Author Message
 Outer scope of a sub inside a sub

Could you elaborate on the subtlety related to scoping demonstrated
by the listing below?

Routine run_test_1 contains a definition of sub filter().
Filter is then passed in a call to add_elem() as a code reference.

run_test_2 uses a different syntax:
   my $filter = sub { ...
and then passes variable $filter.

They work identical in the first run. In the second run, however,

the variable defined in the body of run_test_1.

Full disclosure: perl emits the following warning:

#!/usr/bin/perl
use strict;
use warnings;
use Scalar::Util qw/refaddr/;

sub add_elem {
        $_[0]->();

Quote:
}

sub run_test_1 {

        sub filter {


        }

        add_elem( \&filter, "abc" );
        add_elem( \&filter, "abc" );

Quote:
};

sub run_test_2 {

        my $filter = sub {


        };

        add_elem( $filter, "abc" );
        add_elem( $filter, "abc" );

Quote:
};

print "--- Test 1 ---\n";
run_test_1();
run_test_1();

print "--- Test 2 ---\n";
run_test_2();
run_test_2();



Sun, 14 Apr 2013 16:45:46 GMT  
 Outer scope of a sub inside a sub

Quote:
> Could you elaborate on the subtlety related to scoping demonstrated
> by the listing below?

> Routine run_test_1 contains a definition of sub filter().
> Filter is then passed in a call to add_elem() as a code reference.

> run_test_2 uses a different syntax:
> ? ?my $filter = sub { ...
> and then passes variable $filter.

> They work identical in the first run. In the second run, however,

> the variable defined in the body of run_test_1.

> Full disclosure: perl emits the following warning:

> #!/usr/bin/perl
> use strict;
> use warnings;
> use Scalar::Util qw/refaddr/;

> sub add_elem {
> ? ? ? ? $_[0]->();

> }

> sub run_test_1 {

> ? ? ? ? sub filter {


> ? ? ? ? }

> ? ? ? ? add_elem( \&filter, "abc" );
> ? ? ? ? add_elem( \&filter, "abc" );

> };

> sub run_test_2 {

> ? ? ? ? my $filter = sub {


> ? ? ? ? };

> ? ? ? ? add_elem( $filter, "abc" );
> ? ? ? ? add_elem( $filter, "abc" );

> };

> print "--- Test 1 ---\n";
> run_test_1();
> run_test_1();

> print "--- Test 2 ---\n";
> run_test_2();
> run_test_2();

You'll need to use an anonymous sub instead of
a named one in both.

Add this pragma:  'use diagnostics -verbose;',
to see a full explanation of the error.

--
Charles DeRykus



Sun, 14 Apr 2013 17:50:29 GMT  
 Outer scope of a sub inside a sub

Quote:
> Could you elaborate on the subtlety related to scoping demonstrated
> by the listing below?

> They work identical in the first run. In the second run, however,

> the variable defined in the body of run_test_1.

> Full disclosure: perl emits the following warning:

> #!/usr/bin/perl
> use strict;
> use warnings;
> use Scalar::Util qw/refaddr/;

> sub add_elem {
> ? ? ? ? $_[0]->();

> }

> sub run_test_1 {

> ? ? ? ? sub filter {


> ? ? ? ? }

> ? ? ? ? add_elem( \&filter, "abc" );
> ? ? ? ? add_elem( \&filter, "abc" );

> };

> sub run_test_2 {

> ? ? ? ? my $filter = sub {


> ? ? ? ? };

> ? ? ? ? add_elem( $filter, "abc" );
> ? ? ? ? add_elem( $filter, "abc" );

> };

> print "--- Test 1 ---\n";
> run_test_1();
> run_test_1();

> print "--- Test 2 ---\n";
> run_test_2();
> run_test_2();


new element onto it, yet not right before you query it for its
length.  Consider adding the line:


inside both the filter() and $filter subroutines.  With this change, I
see this output:

--- Test 1 ---
4639308
4639308
4639308
Total number of elements 2
4639308
4639308
10075924
Total number of elements 0
--- Test 2 ---
4848924
4848924
4848924
Total number of elements 2
4848924
4848924
4848924
Total number of elements 2







run_test_1() is called.  They only match the first time run_test_1()
is called.

   As for run_test_2(), the $filter subroutine is constructed every

the previous line.


in run_test_2() always has the same refaddr (which is probably what
you're seeing as well).  But that's only happening because it gets
cleaned up (as there are no more references to it), and further calls

it has been freed.

   To prevent the same memory space from being re-used, declare a

line:





   I hope this helps, Koszalek.

   -- Jean-Luc



Sun, 14 Apr 2013 20:40:23 GMT  
 Outer scope of a sub inside a sub

Quote:

>Could you elaborate on the subtlety related to scoping demonstrated
>by the listing below?

>Routine run_test_1 contains a definition of sub filter().
>Filter is then passed in a call to add_elem() as a code reference.

>run_test_2 uses a different syntax:
>   my $filter = sub { ...
>and then passes variable $filter.

>They work identical in the first run. In the second run, however,

>the variable defined in the body of run_test_1.

>Full disclosure: perl emits the following warning:


The easiest way to see it is to put some debug statements in.

filter() stays in the first scope of test1
the first time its called and everytime.

$filter is a new instance of sub {} and
is new to test2 everytime its called.

Btw, you are not pushing anything but undef's.

-sln

use strict;
use warnings;
use Scalar::Util qw/refaddr/;

sub add_elem {
        $_[0]->( $_[0] );

Quote:
}

sub run_test_1 {


        sub filter {


                printf "     my sub addr = %d\n", refaddr $_[0];
        }

        add_elem( \&filter, "abc" );
        add_elem( \&filter, "abc" );

Quote:
};

sub run_test_2 {


        my $filter = sub {


                printf "     my sub addr = %d\n", refaddr $_[0];
        };

        add_elem( $filter, "abc" );
        add_elem( $filter, "abc" );

Quote:
};

print "\n\n\n--- Test 1 ---\n";
run_test_1();
run_test_1();

print "\n\n\n--- Test 2 ---\n";
run_test_2();
run_test_2();

__END__


--- Test 1 ---



     my sub addr = 25631444

     my sub addr = 25631444
Total number of elements 2, addr elems = 25453388



     my sub addr = 25631444

     my sub addr = 25631444
Total number of elements 0, addr elems = 2272444

--- Test 2 ---



     my sub addr = 2272124

     my sub addr = 2272124
Total number of elements 2



     my sub addr = 25651196

     my sub addr = 25651196
Total number of elements 2



Sun, 14 Apr 2013 23:10:08 GMT  
 Outer scope of a sub inside a sub
Quote:

> Full disclosure: perl emits the following warning:


...

Quote:

> sub run_test_1 {



time run_test_1 is called, it uses this location set up at compile time.
  But on subsequent calls, it sets a new location.

Quote:

>    sub filter {


>    }

At compile time, this glombs onto the same memory location as was used

recompiled, and so does not re-glomb onto the new addresses when new
addresses come into existence.

Quote:

> sub run_test_2 {

>    my $filter = sub {


>    };

This is recompiled for each invocation of run_test_2, and so re-glombs

use each time it recompiled. (I don't think it is recompiles in full,
they use shortcuts that allows it to re-glomb, with full recompilation.
  But it behaves as if it were recompiled)

Xho



Mon, 15 Apr 2013 02:31:57 GMT  
 Outer scope of a sub inside a sub

Quote:

>Could you elaborate on the subtlety related to scoping demonstrated
>by the listing below?

>Routine run_test_1 contains a definition of sub filter().
>Filter is then passed in a call to add_elem() as a code reference.

>run_test_2 uses a different syntax:
>   my $filter = sub { ...
>and then passes variable $filter.

>They work identical in the first run. In the second run, however,

>the variable defined in the body of run_test_1.

That is one of the biggest warts in Perl, in my not so humble opinion.
The second is behaving in the way the first should have behaved, and as
it does behave in other more sane languages that have nested subs, and
as it behaves in Javascript.

The first is... well... an abomination.

How it behaves is that the nested sub, even though it is defined inside
a sub, is still a global definition. Test it out, in the second example,
the sub filter is still accessible *outside* the scope of run_test_1.
And it is defined once. It's a closure bound to the variables in the
first invocation of the run of the outer sub. So every time you run
filter, it will access the same, now anonymous, variables, be it if you
run it outside of the outer sub, or inside it.

Why it behaves like that? Well, I'm not sure, but I think it was related
to how BEGIN and END (and INIT, and CHECK...) blocks are actually subs,
and you can have sub definitions inside them. Those subs have to behave
globally, as if they were not inside the BEGIN block. Something like
that.

I once chatted to a few of the top P5P people about it, and they said it
was *never* going to change.

Like I said, I think it's an abomination. I think BEGIN, INIT, CHECK
etc. should be special cased, not just subs, to behave like this, and
normal subs should behave differently: the inner sub should really be
only visible inside the scope of the outer sub, be it with local (so
it'll also be visible in subs that you call from within the outer sub),
or, more sane, truely lexical (the way your second example behaves). But
lexical subs don't exist, unfortunately.

BTW here's an example using local:

    sub run_test_3 {

        local *filter = sub {


        }
        ...
    }

That local assignment should happen first when the sub is called, even
if the "sub filter" definition in the source happens to be in the end of
the outer sub.

I'm not sure itll behave properly if you depend on \&filter, though...
But plain `filter(...)` invocations should behave well.

--
        Bart.



Mon, 15 Apr 2013 04:44:32 GMT  
 Outer scope of a sub inside a sub

Quote:

> >Could you elaborate on the subtlety related to scoping demonstrated
> >by the listing below?

> That is one of the biggest warts in Perl, in my not so humble opinion.
> The second is behaving in the way the first should have behaved, and as
> it does behave in other more sane languages that have nested subs, and
> as it behaves in Javascript.

Thanks to everyone who posted a reply.
A very interesting thread IMO :-)

A.



Mon, 15 Apr 2013 06:37:45 GMT  
 Outer scope of a sub inside a sub

Quote:
>> sub run_test_1 {


> time run_test_1 is called, it uses this location set up at compile time.
>   But on subsequent calls, it sets a new location.

                             ^^^^^^^^^^^^^^^^^^^^^^

I think this is misleading.  The whole point of pre-allocating "the
location" is an optimization - "the location" (the container
referenced by the lexical) is USUALLY reused in subsequent calls.
Only if the old container is used by something else (has a refcount of
2 or more - e.g., a reference to it is stored somewhere) a new
container is allocated for the lexical.


   stores a reference in the compile tree of &filter.]

Quote:
>>        sub filter {


>>        }
> At compile time, this glombs onto the same memory location as was used

> recompiled, and so does not re-glomb onto the new addresses when new
> addresses come into existence.

There is no such thing as "recompilation" (the whole point of closures
is that they are cheap; what you think of is IMO a
poor-man-implementation of closures via eval "sub {...}").  The
"re-SOMETHING" which may happen is a reallocation of lexicals.  THIS
is what happens when "sub" is executed at run time, as here:.

Quote:
>> sub run_test_2 {

>>        my $filter = sub {


>>        };

Yours,
Ilya


Mon, 15 Apr 2013 08:45:52 GMT  
 Outer scope of a sub inside a sub

Quote:

> >Could you elaborate on the subtlety related to scoping demonstrated
> >by the listing below?

>   [snip]
> ...
> >They work identical in the first run. In the second run, however,

> >the variable defined in the body of run_test_1.
> That is one of the biggest warts in Perl, in my not so humble opinion.
> The second is behaving in the way the first should have behaved, and as
> it does behave in other more sane languages that have nested subs, and
> as it behaves in Javascript.

> The first is... well... an abomination.

> How it behaves is that the nested sub, even though it is defined inside
> a sub, is still a global definition. Test it out, in the second example,
> the sub filter is still accessible *outside* the scope of run_test_1.
> And it is defined once. It's a closure bound to the variables in the
> first invocation of the run of the outer sub. So every time you run
> filter, it will access the same, now anonymous, variables, be it if you
> run it outside of the outer sub, or inside it.

At least there's a warning:

    (W closure) An inner (nested) named subroutine
    is referencing a lexical variable defined in
    an outer named subroutine.

I think the issue's subtle enough that the above
warning could/should add a couple of pointers:

     See: 'use diagnostics -verbose'.
          Also perlsub/perlmod.

Since 'use diagnostics -verbose' is much completer:

    When the inner subroutine is called, it will
    see the value of the outer subroutine's variable
    as it was before and during the *first* call to
    the outer subroutine; in this case, after the
    first call to the outer subroutine is complete,
    the inner and outer subroutines will no longer
    share a common value for the variable.  In other
    words, the variable will no longer be shared.

    This problem can usually be solved by making the
    inner subroutine anonymous, using the sub {}
    syntax.  When inner anonymous subs that reference
    variables in outer subroutines are created, they
    are automatically rebound to the current values
    of such variables.

- Show quoted text -

Quote:
> Why it behaves like that? Well, I'm not sure, but I think it was related
> to how BEGIN and END (and INIT, and CHECK...) blocks are actually subs,
> and you can have sub definitions inside them. Those subs have to behave
> globally, as if they were not inside the BEGIN block. Something like
> that.

> I once chatted to a few of the top P5P people about it, and they said it
> was *never* going to change.

> Like I said, I think it's an abomination. I think BEGIN, INIT, CHECK
> etc. should be special cased, not just subs, to behave like this, and
> normal subs should behave differently: the inner sub should really be
> only visible inside the scope of the outer sub, be it with local (so
> it'll also be visible in subs that you call from within the outer sub),
> or, more sane, truely lexical (the way your second example behaves). But
> lexical subs don't exist, unfortunately.

And perlsub/perlmod would help clarify that as well:

    The "BEGIN", "UNITCHECK", "CHECK", "INIT"
    and "END" subroutines are not so much subroutines
    as named special code blocks, of which you can have
    more than one in a package, and which you can not
    call explicitly. See "BEGIN, UNITCHECK, CHECK, INIT
    and END" in perlmod

    These code blocks can be prefixed with "sub" to
    give the appearance of a subroutine (although this
    is not considered good style). One should note
    that these code blocks don't really exist as named
    subroutines (despite their appearance). The thing
    that gives this away is the fact that you can have
    more than one of these code blocks in a program,
    and they will get all executed at the appropriate
    moment. So you can't execute any of these code
    blocks by name.

--
Charles DeRykus



Mon, 15 Apr 2013 13:22:28 GMT  
 Outer scope of a sub inside a sub

Quote:


>> At compile time, this glombs onto the same memory location as was used

>> recompiled, and so does not re-glomb onto the new addresses when new
>> addresses come into existence.

> There is no such thing as "recompilation" (the whole point of closures
> is that they are cheap; what you think of is IMO a
> poor-man-implementation of closures via eval "sub {...}").

Sure there is such a thing as recompilation.  Just put it in a string
eval.  It isn't recompiled in *this* context, of course, which is what I
said.

Xho



Tue, 16 Apr 2013 02:45:49 GMT  
 Outer scope of a sub inside a sub

Quote:


>>> At compile time, this glombs onto the same memory location as was used

>>> recompiled, and so does not re-glomb onto the new addresses when new
>>> addresses come into existence.

>> There is no such thing as "recompilation" (the whole point of closures
>> is that they are cheap; what you think of is IMO a
>> poor-man-implementation of closures via eval "sub {...}").

> Sure there is such a thing as recompilation.  Just put it in a string
> eval.  It isn't recompiled in *this* context, of course, which is what I
> said.

Mea culpa, I quoted a wrong paragraph.  I should have stated it "like
this":

Quote:
>> sub run_test_2 {

>>       my $filter = sub {


>>       };

> This is recompiled for each invocation of run_test_2, and so re-glombs

> use each time it recompiled. (I don't think it is recompiles in full,
> they use shortcuts that allows it to re-glomb, with full
> recompilation.  But it behaves as if it were recompiled)

There is no such thing as "recompilation" (the whole point of closures
is that they are cheap; what you think of is IMO a
poor-man-implementation of closures via eval "sub {...}").

Sorry,
Ilya



Tue, 16 Apr 2013 23:51:04 GMT  
 
 [ 11 post ] 

 Relevant Pages 

1. Error : calling sub routine inside another sub routine

2. anonymous sub inside anonymous sub?

3. package wants outer subs

4. Changeing Scope of a Sub Reference

5. scope for recursive sub

6. lvalue sub doesn't work inside perl debugger

7. Passing file descriptor to sub inside a package?

8. oo programming and sub routines inside module

9. Bug -- variable corruption inside sub/foreach

10. Getting the name of a function (sub) inside it

11. Modifying a variable inside a sub

12. using Find::file inside a sub-routine ..?

 

 
Powered by phpBB® Forum Software