If you declare named subroutines within other subroutines, the value of lexical variables declared in the outter subroutine will not necessarily stay in sync with the value of that variable in the inner subroutine (if you ran this code with warnings, you would get a message saying "Variable $indent will not stay shared"). From perldoc perldiag:

When the inner subroutine is called, it will probably 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.

Furthermore, if the outer subroutine is anonymous and references a lexical variable outside itself, then the outer and inner subroutines will never share the given variable.

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 called or referenced, they are automatically rebound to the current values of such variables.

This may not be a problem with the code you posted, since handle_start and handle_end will (in theory) always be called in pairs and never be called except by XML::Parser. However, doing this in other parts of your code could result in unexpected results. For example this code:

use strict; use warnings; print "calling outter from main:\n"; outter(); print "calling inner from main:\n"; inner(); print "calling outter from main:\n"; outter(); sub outter { my $var = 0; print " outter: $var\n"; print " calling inner, should increment 0 -> 1\n"; inner(); sub inner { print " inner: $var -> "; $var++; print "$var\n"; } }
produces the following output:
Variable "$var" will not stay shared at sub_in_sub.pl line 23. calling outter from main: outter: 0 calling inner, should increment 0 -> 1 inner: 0 -> 1 calling inner from main: inner: 1 -> 2 calling outter from main: outter: 0 calling inner, should increment 0 -> 1 inner: 2 -> 3

Notice that when inner() is called from main, the value of $var is still 1, and the second time outter() is called the inner() sub starts with a value of 2 for $var instead of 0, as you might expect.

Arunbear's suggestion declared the $indent variable and both handler subs inside a bare block. This limited the scope of $indent to just those two subs, but it doesn't suffer from the sharing problem described above. If you really want to declare subs within subs, look into closures.

Thanks to tye for helping me find the reference to this in the docs.

HTH


In reply to Re^3: best practice when using XML::Parser and strict. by bobf
in thread best practice when using XML::Parser and strict. by reasonablekeith

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.