Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical

Re: Static variables (and also Perl 6)

by clintp (Curate)
on Jan 26, 2002 at 05:13 UTC ( #141706=note: print w/replies, xml ) Need Help??

in reply to Static variables (and also Perl 6)

Last week when I read Apocalypse 4 I had terrible visions of what the elimination of "bare blocks" would mean for "static" variable declarations. I wasn't the only one confused, so I wrote a note to Larry and Damian asking that this notion be cleared up in the next exegesis.

This is the jist of the conversation relating to this topic. Damian: I hope you don't mind the reprinting of your mail, I'm sending a note to you asking forgiveness rather than permission in advance:

I asked:

We think (and maybe the Exegesis can clarify) that this means the elimination of two<super>*</super> succinct, obvious, and useful idioms. The declaration of C static-like variables ("compile-time" initializations):
{ my $foo=512; sub something { $foo++; } }
And other stuff. Damian's reply was:
That's now:
for 1 { my $foo=512; sub something { $foo++; } }
INIT { my $foo=512; sub something { $foo++; } }
After I had whined:
Cluttering up these expressions with do {} and for 1 {} seems like syntatic nonsense and misdirection.
He said:
No, it's just a reflection of the fundamental change that a block is now *always* a closure (and hence a value). I actually think that the INIT {...} or even the BEGIN {...} approach is a much safer way to specify such things anyway, as it guarantees that the shared lexical (a lovely idiom, I agree) is initialized before any potential use of the subroutines that share it.
I hope this sheds a little light on the topic. I know it gave me a little more positive outlook on Exegesis 4.

<super>*</super>The other I asked about was shared lexicals between subs: { my $foo; sub bar{} sub baz{} }

Replies are listed 'Best First'.
(tye)Re: Static variables (and also Perl 6)
by tye (Sage) on Jan 26, 2002 at 12:22 UTC

    Thanks. That is interesting.

    This also resolves one mod_perl problem. mod_perl basically warps each CGI script so that it becomes a subroutine so that it can be called over and over again without having to be recompiled. You can simulate this like so:

    #!/usr/bin/perl -w use strict; sub mod_perl { { my $static1; print "[", defined($static1) ? $static1 : "undef", "] "; BEGIN { $static1= "Hello" } sub sub1 { my $old= $static1; $static1= shift if @_; return $old; } } BEGIN { my $static2= "Hi"; print "<$static2> "; sub sub2 { my $old= $static2; $static2= shift if @_; return $old; } } print "(",sub1(shift),") "; print "(",sub2(shift),")\n"; } mod_perl(qw( Goodbye Bye )); mod_perl(qw( Morning Morn )); mod_perl(qw( Evening Eve )); mod_perl();
    which produces the following output:
    Variable "$static1" will not stay shared at line 9. <Hi> [Hello] (Hello) (Hi) [undef] (Goodbye) (Bye) [undef] (Morning) (Morn) [undef] (Evening) (Eve)
    which shows that the BEGIN (on the outside) has removed the "won't stayed shared problem".

    Of course, it doesn't solve the ``reinitialization each time the "script" is "rerun"'' problem, but then my proposal didn't address that either.

    And I'd still like to be able to create static variables without so much extra baggage (a whole extra block and having to move the variable outside of the subroutine).

    It'd be nice to be able to put static variable in inner blocks even:

    sub uniqueNonBlanks { my( $avItems )= @_; my @return; for my $item ( @$avItems ) { my %seen= ( "" => 1 ) BEGIN; push @return, $item unless $seen{$item}++; } return wantarray ? @return : \@return; }
    my @a= uniqueNonBlanks("Why","","I","Why"); my @b= uniqueNonBlanks("Why","You","Be","I"); $"= ","; print "(@a) (@b)\n";
    prints "(Why,I) (You,Be)".

    Oh, BTW, some have asked what a static variable is. It is a lexical variable in a subroutine that doesn't get recreated each time the subroutine is called. It keeps its value between successive calls to the subroutine. So, no matter how many times the subroutine gets called, there is only one instance of the static variable.

    Static variables are useful for the same reasons that singleton classes are useful. It is just that a static variable is trivial to implement in comparison to a singleton class.

    If you haven't missed static variables in Perl, then it probably just because you've been using globals for those cases instead.

    Here is a paraphrase of a real-life example of static variables that shows how globals aren't the best replacement:

    sub validateOptions { my( $hvOptions, $hvUserOpts )= @_; for my $key ( keys %$hvOptions ) { $hvOptions->{$key}= delete $hvUserOpts->{$key} if exists $hvUserOpts->{$key}; } if( %$hvUserOpts ) { my $subName= (caller(1))[3]; croak "$subName: Invalid options (", join(",",keys %$hvUserOpts),")"; } return $hvOptions; } BEGIN { my %defaults= ( Access=>READ_ONLY, Delimiter=>"/" ); sub Open { my( $keyName, $hvUserOpts )= @_; my $hvOpts= validOptions( {%defaults}, $hvUserOpts ); # ... } } BEGIN { my %defaults= ( Delimiter=>"/", Translate=>"text" ); sub Read { my( $handle, $hvUserOpts )= @_; my $hvOpts= validOptions( {%defaults}, $hvUserOpts ); # ... } }
    If I used globals I'd have to replace the two %defaults static variables with two globals with different names:
    our %Open_defaults= ( Access=>READ_ONLY, Delimiter=>"/" ); sub Open { my( $keyName, $hvUserOpts )= @_; my $hvOpts= validOptions( {%Open_defaults}, $hvUserOpts ); # ... } our %Read_defaults= ( Delimiter=>"/", Translate=>"text" ); sub Read { my( $handle, $hvUserOpts )= @_; my $hvOpts= validOptions( {%Read_defaults}, $hvUserOpts ); # ... }
    which makes the code harder to modularize and maintain.

    So I'd still like BEGIN as a statement modifier...

    But what about the other mod_perl problem. The current way to solve that is only use globals:

    sub mod_perl { our $static= "Hi"; sub sub1 { my $old= $static1; $static1= shift if @_; return $old; } # ... }
    because now the initialization will be done each time the "script" is "rerun", that is, each time mod_perl() is called. And we won't get "won't stay shared" problems because globals are static; there is only ever one instance of a global variable, so that instance is always the one that is shared.

    But we'd rather not use globals if we can avoid them so that we don't have to worry about collisions in the names of two static variables from far-flung ends of the same package.

    So we'd really like something that works like:

    sub mod_perl { { my $static1 BEGIN; $static1= "Hi"; sub sub1 { my $old= $static1; $static1= shift if @_; return $old; } } # ... }
    but it'd be even cooler to be able to write that as:
    sub mod_perl { sub sub1 { my $static1= "Hi" STATIC; my $old= $static1; $static1= shift if @_; return $old; } # ... }
    where STATIC tells Perl to create an instance of $static1 only once (at compile time) but to reinitialize that variable each time the run-time pass hits the definition of "sub sub1". Currently, the definition of "sub sub1" doesn't even exist once we get to "run time". But the presense of a STATIC variable inside of a nested subroutine would tell Perl to replace the "sub sub1" definition with $static1= "Hi" in the compiled byte code. (If the subroutine was not nested, then the STATIC variable would simply be initialized at compile time and no run-time code would be generated.)

            - tye (but my friends call me "Tye")
      Try this out:
      my $static := BEGIN {my $foo = "default value"};
      If I am not mistaken, the BEGIN block should initialize its variable at compile time, and always return the same variable thereafter. The my variable that you are declaring is then made an alias to this returned variable. Because it is declared lexically, it is available in a lexical scope only.

      If that doesn't work, then some fairly straightforward variant on it should.

      This construct is embeddable in any scope you want. And so you can embed it in inner blocks as well. Figure out how to add syntactic sugar, and spice to taste. :-)

        Yep. I believe that that works exactly as tilly suggested. Way cool. Wish I'd thought of it. Here's an interesting variant:
        my ($static1, $static2) := *BEGIN{["val1","val2"]};
        (Note the implicit enreferencing of the array reference returned by the BEGIN block, which happens because the "flattening *" expects an array.)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://141706]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (3)
As of 2017-09-21 22:48 GMT
Find Nodes?
    Voting Booth?
    During the recent solar eclipse, I:

    Results (254 votes). Check out past polls.