in reply to Re^3: sub fuction inside sub functioin?
in thread sub fuction inside sub functioin?

Recursions with inner subs are a very special case! I agree this should be handeld with care, unfortunately reference counting is not the best garbage collection technique...

but using the global namespace for an inner function is somehow dangerous. What if you call an external function within outer, that tries to call a completely different function also called inner()?

Maybe you should better use package to choose another namespace within outer() in combination with this local *name strategie to prevent this trap!

Cheers Rolf

UPDATE: Well OK, at least you get a warning ...

$\="\n"; sub inner { print "global_inner" } inner(); sub whatever { inner() } sub outer { local *inner=sub {print "inner_of_outer"}; inner(); whatever(); } outer(); __END__ Subroutine main::inner redefined at c:\perl\push.pm line 9. global_inner inner_of_outer inner_of_outer

Replies are listed 'Best First'.
Re^5: sub fuction inside sub functioin?
by ikegami (Patriarch) on Dec 07, 2008 at 23:20 UTC

    but using the global namespace for an inner function is somehow dangerous. What if you call an external function within outer, that tries to call a completely different function also called inner()?

    First, there's no such thing as the global namespace. The code uses the current namespace.

    Secondly, are you seriously complaining that a script can't have two functions with the same name? Or did you mean to test something like

    use strict; use warnings; $\ = "\n"; sub outer2 { local *inner = sub { print "inner_of_outer2" }; inner(); } sub outer1 { local *inner = sub { print "inner_of_outer1" }; inner(); outer2(); } outer1();

    Unfortunately, that gives a warning.

    inner_of_outer1 Subroutine main::inner redefined at script.pl line 7. inner_of_outer2
      I'm "seriously comlaining" that local is another form of global, no matter which namespace.

      There is no big point for using nested subs if they are not completely encapsulated and might interfere with external names, which you might not be able to control.

      IMHO safer alternatives are:

      # No Namespace Confusion! $\="\n"; sub inner { print "global_inner" } inner(); sub whatever { inner() } sub outer { { package outer; local *inner=sub {print "inner_of_outer"}; inner(); } whatever(); } outer(); __END__ global_inner inner_of_outer global_inner

      or

      # No Memory Leak! $\="\n"; my $inner=sub { print "global_inner" }; $inner->(); sub whatever { $inner->() } { my $inner; sub outer { $inner=sub {print "inner_of_outer"}; $inner->(); whatever(); } } outer(); __END__ global_inner inner_of_outer global_inner
      I prefere the latter! Unless you plan to call outer() recursively *... ; )

      Good night! Rolf

      UPDATE: (*) and if you really want to do it, you have to simulate the mechanism of local for lexicals, by starting outer() with push @stack,$inner; and ending it with $inner=shift @stack

        I must disagree. Having two variables or functions with the same name in the same scope is a bad idea.

        There is no big point for using nested subs if they are not completely encapsulated

        Some encapsulation is better than no encapsulation. The extra complications are not needed. Even with the extra complications, it's still not completely encapsulated.

        # No Memory Leak!

        You haven't changed anything. $inner still refers to the sub. The sub still captures $inner if the helper was recursive. The potential for a memory leak hasn't changed at all. You made it more complex for no gain.Eventually, $inner will be assigned a new value and stop referring to the helper. Nevermind.

      Unfortunately, that gives a warning.

      I note that:

      use strict ; use warnings ; our $inner = "SET" ; sub inner { my ($s) = @_ ; print "$s outermost inner \$inner=$inner\n" ; } ; print "outermost start\n" ; inner('') ; buddy('') ; wrapper('') ; buddy('') ; print "outermost end\n" ; sub wrapper { my ($s) = @_ ; print "$s wrapper entered\n" ; inner($s.' ') ; local *inner ### ; print "$s *** separate local\n" ; *inner = sub { my ($s) = @_ ; $inner ||= "NOT SET" ; print "$s innermost inner $inner\n" ; } ; inner($s.' ') ; buddy($s.' ') ; print "$s wrapper exit\n" ; } ; sub buddy { my ($s) = @_ ; print "$s buddy entered\n" ; inner($s.' ') ; print "$s buddy exit\n" ; } ;
      gives:
      outermost start
        outermost inner $inner=SET
        buddy entered
          outermost inner $inner=SET
        buddy exit
        wrapper entered
          outermost inner $inner=SET
      Subroutine main::inner redefined at zz.pl line 29.
          innermost inner SET
          buddy entered
            innermost inner SET
          buddy exit
        wrapper exit
        buddy entered
          outermost inner $inner=SET
        buddy exit
      outermost end
      
      showing the same warning. Also the value of $inner is unaffected by the local -- which may or may not be a surprise.

      But if you remove the ### in the code above, to make that line:

      local *inner ; print "$s *** separate local\n" ; *inner
      that gives:
      outermost start
        outermost inner $inner=SET
        buddy entered
          outermost inner $inner=SET
        buddy exit
        wrapper entered
          outermost inner $inner=SET
        *** separate local
          innermost inner NOT SET
          buddy entered
            innermost inner NOT SET
          buddy exit
        wrapper exit
        buddy entered
          outermost inner $inner=SET
        buddy exit
      outermost end
      
      NB: using the stand alone local *inner is undefining all parts of *inner.

      I guess that the '=' is doing special things to assign the rhs to the right part of the lhs GLOB, so this is not a run of the mill assignment. Could this be why the presence of the initialiser appears to change the effect of local on a GLOB ?

      You will recall that in another thread Setting signal handlers considered unsafe? it was established that  local $SIG{ALRM} = ... that the local sets the lhs undef before the assignment takes place. Which this isn't consistent with !