Stas Bekman <[EMAIL PROTECTED]> writes:

> Brian McCauley wrote:
> [...]
> >>Nice, but:
> >>
> >> > +The easiest and the fastest way to solve the nested subroutines
> >> > +problem is to change C<my> to C<local> C<our> for all variables for
> >> > +which you get the warning.  The C<handler> subroutines are never
> >>...
> >>[...]
> >> > +  local our $counter = 0;
> >>
> >>local our? That should be either local or our, but not both.
> > No.
> >
> >>Do I miss something?
> > Yes.  (I tried to explain this in Paris but I was in danger of
> > causing
> > you to miss lunch completely).
> > local() and our() do two quite separate and complementary things.
> > our() (in effect) declares a lexically scoped alias for a package
> > variable.
> > local() restores the old value of a package variable (usually undef)
> > at the end of the current lexical scope.
> 
> In effect you use local() to undef the variable, instead of explicitly
> initializing it. Why not doing this explictly?

Firstly it's conceptually neater to use local.  I want to think of the
variable as local rather than as a global variable that needs to be
explicitly reset.

Secongly it's a smaller change.

Thirdly you can never be sure the undef would be reached. (I realise
after reading the rest of your mail that that statement will not make
any sense to you because you've misunderstood what local does and
hense where the undef would need to go).

> so instead of replacing:
> 
> my $counter;
> 
> with:
> 
> local our $counter;
> 
> it's probably better to say:
> 
> our $counter = 0;

Surely you meant:

 undef our $counter;

Or 

  our $counter = undef.

In this case $counter is treated a a number so it's OK to use 0 but
the aim of the game is to come up with a drop-in replacement for all
lexically scoped variables that suffer "will not remain shared".

Anyhow, local() does something quite different.  It undefines it upon
exit of the current scope.  Obviously _most_ of the time it matters
little if the variable is undef on exiting the scope or re-entering it
next time.  But why bother storing up troubles.  Better to do the
right thing from the outset.

> or if you insist on using both:
> 
> our $counter;
> local $counter; # undef $counter

Why split them?  Doesn't aid readability.  Is a pain if the original
my() was inside an expression.

The comment is confusing.  It implies that local performs
initialization.  It doesn't.  It performs finalization.

The comment should read:

 local $counter; # Automatically undef $counter when script terminates

> later on I show why this is better for user's understanding.

Really?

> > The two combined therefore give a package variable two of the most
> > useful properties of a lexical one.  Of course a real lexical variable
> > doesn't really become undefined when it does out of scope - it really
> > becomes anonymous, and iff there are no remaining (unweakened)
> > references it then gets GCed.  But for file-scoped lexicals in the
> > main script file the difference is usually not that important.  Both
> > effectively get killed at the point where global destruction would
> > have taken place.
> >
> >>The rest looks good, but that's not the simplest solution as you have
> >>to modify the variables.
> > Is there a simpler one?  For a typical script with say half a dozen
> > variables the "would not remain shared" the "local our" solution
> > requires a dozen keystokes on each of half a dozen lines.
> 
> Don't forget that our() is not available before perl 5.6. So we can't
> quite eliminate the previous solution unless you suggest to go with a
> back-compatible version:
> 
> use vars qw($counter);
> local $counter;

I thought enough time has gone by that 5.6 can be considered the norm
and the tiny fraction of people doing on-going work on legacy pre-5.6
system should be expected to be familar with the work-rounds required.
On that basis I concuded that the work-rounds needed to get arround
the lack of our() in 5.5 are outside the scope of the the porting
document and belonged in the perl_reference.  In my (as yet
incomplete) revision of the perl_reference document I include mention
of 'use vars'.  If you think this should be in porting I'm not going
to argue.

> and of course the proper solution is:
> 
> use vars qw($counter);
> $counter = 0; # or undef

No, the proper solution is:

  local our $counter;
 
IMNSHO "use vars" is not the proper solution, it is a backward
compatability work-round for people still using old versions of Perl
that prevent them using the proper solution.

Initialization is not the proper solution.  In some cases, like the
case of the simple counter you can get away with using initialization.
But the proper solution is finalization (using local).  To see why
consider a CGI script that contains, at file scope:

  open my $file, '>', $outfile or die "$outfile: $!";

What happens if you change that to:

  use vars qw($file);
  $file = undef;
  open $file, '>', $outfile or die $!;

Well firstly it looks way ugly :-) !  Much more importantly it doesn't
close the file when the script terminates.

  open local our $file, '>', $outfile or die $!;

This is a much smaller change to the original script.  It looks much
cleaner.  And most importantly it actually works.

> >>Granted, the original "simplest" solution has its troubles.
> > The original "simplest" solution involved finding (and subsequently
> > maintaining) a globally unique filename then splitting the program in
> > to two parts.  Thereafer you have to maintain two files even on CGI
> > servers.  I would contend that this "simple solution" is not simple.
> > If you are going to all that troble you may as well to the extra
> > 804.65m and produce a proper mod_perl handler and a small wrapper to
> > make it work also in a CGI environment.  Also, as of mod_perl2, the
> > "simple solution" is not even, as it stands, a solution as it relied
> > on the script being in the CWD.
> 
> Remember, we are talking about mp1 guide patching. Not everything that
> applies to mp1 applies to mp2.

OK, please forget I ever mentioned mp2.

> [...] I hope that the problem with CWD will be resolved [...]

The problem with CWD is not that simple.

The trouble with advising people to create Perl4-style library in the
same diectory as the script is that it is not intuatively obvious that
the leafname of that library needs to unique for each ported CGI
script.  That's not just unique within the directory.  It's not even
just unique accross the whole server. It needs to be unique across all
servers on which the script may ever be installed!

This namespace problem is not new to Perl programmers.  It is, indeed,
exactly the same problem as the naming of modules.  So why not spend
the extra minuite or so of effort to make a proper Perl5-style library
(i.e. a module using Exporter) instead and reduce two problems to one.

Furthermore consider an inexperienced Perl programmer who rips the
guts out of a GCI script into a Perl4-style library.  There's a strong
chance that he'll then think "hey, I've got this library now, let's
use it in another CGI script too".  This, of course, will produce a
script that works under CGI but fails sporadically under mod_perl.

I'm not inventing these problems.  I've dealt with the fallout from
the current porting guide in comp.lang.perl.* a few times.

Far better not to expose people to the Perl4-style library solution at
all (except perhaps to warn them against it).

In the post-patch version of 'porting' I give them a simple choice:

First choice - move the guts of your CGI into modules.  (This is in
fact 95% of the way to porting your script to a native mod_perl
hander).

Second choice - leave the script 99.9% untouched but use localised
package variables for any variables that are, in the design of the
script, already global.

> If you think that using globals + their initialization is a better
> solution,

No, initialization is not a good solution.  Finalisation is what is
wanted.

The use of term "global" to refer to Perl's package variables is
misleading.

>From the point of view of program design file-scoped lexicals are
global variables.  By suggesting the use of package variables I'm not
suggesting that anyone start using the programming technique of global
variables in place of something else.  What we are dealing with here
is the situation where one has a CGI script that already has a design
that uses global variables.  These global variables happen to be
implemented as file-scoped lexicals.

To port this script to run as a mod_perl registry script the
_simplest_ change is just to choose to implement these variables as
localised package variables instead.

> [...] we can replace the lib.pl solution with it,

Good.

> but should add it to the perl reference section.

I can't work out what "it" referrs to above.  Both solutions should
appear in the perl reference section.  The lib.pl solution should not
appear in porting, and even if it did then it defintely should not be
described by saying: "This solution provides the easiest and the
fastest way to solve the nested subroutines problem, since all you
have to do is to move the code into a separate file,..." because
that's simply untrue (on two counts).

"Move the code into a separate file" is not "all you have to do".
Before you can do that you have to choose a globally unique filename
for your file.  And remember I mean _globally_ unique - it must not
clash with one used in any other registry script on any server where
the script will ever be installed.

Even if "move the code into a separate file" _was_ all you had to do
that would still be a couple of orders of magnitude more effort than
the "local our" solution thus it would still not be "the easiest and
the fastest way".


-- 
Reporting bugs: http://perl.apache.org/bugs/
Mail list info: http://perl.apache.org/maillist/modperl.html

Reply via email to