Beefy Boxes and Bandwidth Generously Provided by pair Networks Joe
go ahead... be a heretic
 
PerlMonks  

How will you use state declared variables in Perl6?

by Limbic~Region (Chancellor)
on Jul 08, 2005 at 20:21 UTC ( #473561=perlmeditation: print w/ replies, xml ) Need Help??

All,
For C programmer's, declaring variables in Perl6 with state won't seem very foreign - just think static. In a nutshell, declaring a variable with state only initializes the variable once and any time the sub is re-invoked the variable retains its previous value. Update: This is inaccurate - read the authorative explanation.

Here is an example I, and others, wrote before Pugs had OO.

#!/usr/bin/pugs # Demo of the state() variable declaration. # This is also a neat way of doing OO without actually having OO avail +able. use v6; sub gen_cashier () { # This variable corresponds to a class variable. # It is shared across all "instances" of gen_cashier(). state $cash_in_store = 0; # One could add my() variables here, which correspond to instance +variables. # These would not be shared. # Finally, we return a hashref which maps method names to code. return { add => { $cash_in_store += $^amount }, del => { $cash_in_store -= $^amount }, bal => { $cash_in_store }, }; } my $drawer; $drawer[$_] = gen_cashier() for 1..3; $drawer[1]<add>( 59 ); $drawer[2]<del>( 17 ); say $drawer[3]<bal>(); # This should say "42"

The place I think it would come in most handy is for caching*. In p5, if a cache variable (%seen for instance) is supposed to belong to a sub that needs to persist across invocations, you need to do things like:

  • Pass the cache variable as an argument
  • Scope the variable at the package level
  • Use naked blocks to create a closure

In Perl6, it will be as simple as:
sub foo () { state %seen; # ... }

So how will you use state declared variables in Perl6?

Cheers - L~R

The cache variable doesn't necessarily need to be used to calculate the return value of the sub which would be made easy using Memoize.

Comment on How will you use state declared variables in Perl6?
Select or Download Code
Re: How will you use state declared variables in Perl6?
by mirod (Canon) on Jul 08, 2005 at 20:44 UTC

    As you described: every time I have something similar to this in Perl 5, and that's quite often:

    { my $var; BEGIN { $var= 'init_val'; } sub foo { ... ... $var ... ... } }
Re: How will you use state declared variables in Perl6?
by BrowserUk (Pope) on Jul 08, 2005 at 21:07 UTC
    So how will you use state declared variables

    With great caution!

    Globals are trouble, no matter how you label them. Closures are much safer.

    In your example, foo() is a global sub, so there can never be two instances of it, but if foo() was a method, multiple instances of whatever class foo() was a method of, would all share the same %seen. Effectively, %seen becomes a Class variable. These can be useful--eg. counting the number of instances created or in existiance--but why not use a Class variable instead?

    The danger lies in that if I inherit a new class from that class, instances of both classes will access and modify the single %seen. Whilst there maybe legitimate reasons to do this, they are much rarer and harder to see, as whatever values are contained in %seen, they will represent a classic mix of apples and oranges.

    This is giving the potential to create the same sort of problems we see in Perl5 with it's global vars. Eg. $| & $/ & $\ -- Having to use one function (select), to control which handle setting $| will affect is a pita.

    However, not having a similar possibility to control which handle, setting applied to $/ and $\ will affect, is worse. The separation and encapsulation of state is what makes OO so useful.

    I can see state vars being used to hold things like DB handles (wrapped inside singleton classes if you must :), but done that way, it stops you from writing singleton factories. I also see problems with even this use.

    If P6 objects are going to be usable across threads, then using state varibles for singletons is going to produce potential problems where, for example, DB handles can only be used from one thread.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
      Effectively, %seen becomes a Class variable

      Not so! A class variable is accessible to all instances of a class. A state variable (assuming it behaves more or less exactly like a static variable in C/C++) is visible only within the declaring function, and if that function is a method, then it is restricted to the instance as well. It's actually a tighter scope than member variables with the same persistence.

        The scope is constrainted to the method, but the potential to affect change to it is not!

        Any instance that can call the method can change it's value. Any method in the same class can call the method, therefore any instance of the class can call the method, and unless it is somehow constrained, any instance of any subclass can call a method that will call a that method and so change it's state.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
      Closures are much safer.

      Perhaps I'm misunderstanding, but I was under the impression that the Perl6 code:

      sub foo { state $bar; $bar++; }

      Was equivalent to the Perl5 code:

      { my $bar; sub foo { $bar++; } }

      Which is a closure if I ever saw one. How is this safer? I'm not trying to blindly disagree with you, I'm honestly curious why the closure is safer.

        I believe BrowserUK's point is that if foo is just a global subroutine, there is no difference. On the other hand, suppose you had
        package Bar; sub new { ... } sub foo { state $bar; $bar++; return $bar; }
        Then later...
        $bar1 = Bar->new(); $bar2 = Bar->new(); $bar1->foo(); # returns 1, as you would expect $bar1->foo(); # returns 2, as you would expect $bar2->foo(); # returns 3, possibly unexpected
        At least this is my understanding. Is this what you meant, BrowserUK?

        I don't think that's the type of closure BrowserUk is referring to. Think more of this perl 5 code:

        sub genclosure { my $bar; return sub { $bar++; } }
        Each time you call genclosure, you get a new closure, a new state variable. You don't reuse the previously existing state variable.

        So I would have to agree with BrowserUk's "very carefully" statement. The same as I would use your perl5 (global) closure code - very carefully. That said, I have probably about a half-dozen or so places where I need or want to cache data globally in my projects at work, so this would be very nice syntactical sugar for that. However, I also generate a few closures where this new idea could easily bite a junior programmer in the arse when they wonder why their data isn't returning properly. ("How did that counter get so high already?")

        That said, I also generally avoided static in C as well, so that's not really surprising for me. After all, one generally looks at programming tasks from the basis of their experience - if it looks like a nail, that may be because all you have is a hammer.

        Update: TimToady's response has cleared things up a bit, thankfully. It was looking a bit grim for state - although there is still some care to be taken. I'll have to think if I can figure out where I'd use this feature, if anywhere.

      BrowserUk,
      Globals are trouble, no matter how you label them. Closures are much safer.

      I am sure that neither you nor I have any confusion as to how state variables in Perl6 will work, but I want to respond to this as I think it may lead others astray. A variable's visibility declared with state is still restricted to the scope it was declared in. The difference from a regular lexicals is that if program execution comes back to that scope the variable is not re-initizalized and its previous value is restored. Update: TimToady has explained it much better.

      In your example, foo() is a global sub, so there can never be two instances of it, but if foo() was a method, multiple instances of whatever class foo() was a method of, would all share the same %seen. Effectively, %seen becomes a Class variable. These can be useful--eg. counting the number of instances created or in existiance--but why not use a Class variable instead?

      You are correct - variables declared with state can be used to simulate class variables just as the example I provided shows. In that case, OO didn't exist yet. Unlike p5, methods and subs will be very distinguishable in Perl6. That should make it easier to distinguish when it is more appropriate to use a class variable - but caution is warranted.

      However, not having a similar possibility to control which handle, setting applied to $/ and $\ will affect, is worse. The separation and encapsulation of state is what makes OO so useful.

      This is fixed in Perl6.

      If P6 objects are going to be usable across threads, then using state varibles for singletons is going to produce potential problems where, for example, DB handles can only be used from one thread.

      The bulk of your reply seems to be focused on how state declared variables may cause problems with OO. Nothing in my post indicated that's how I thought they should be used other than to say if you don't have OO yet - they make simulating it easier. The example I gave was that of a regular sub - not a method. I asked for examples of how others might use it. From your reply, I can assume you won't be using it for OO. Can you think of any other places you might use it for?

      Cheers - L~R

        Hmm. I would need to understand the difference between state and local, and the scope on the namespace in which state variables live.

        Ie. Does state $foo; refer to the same thing across packages, files, modules, threads?

        In single threaded apps, a truely global (process wide) variable type has uses for things like DB handles, file handles, sockets, ports, etc. anything that is a truely process global entity.

        Once you introduce threads into the equations, things get sticky. It very much depends upon the threading models supported by the OS and which of those models is used by libraries supplying services to your process as to whether things like DB handles can be safely used across threads or not.

        One assumes that the state keyword came into being in order to support a particular scenario. A good description of that scenario, and the implications of would be nice to see.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
Re: How will you use state declared variables in Perl6?
by TimToady (Parson) on Jul 08, 2005 at 22:18 UTC
    Well, they aren't really quite the same as C's statics. You should really think of them more as persistent lexicals that also happen to know how to initialize themselves on first use. Unlike globals (and like lexicals) they are by default per-thread and per-clone. (They are not per-instance, even if used in methods. In a sense, "has" variables are state variables on the instance.)

    If used in a class, the state variable does not generate a public accessor unless there is a dot twigil to indicate that. Without the accessor no other class can see the variable, not even derived classes. Only your class's methods can see it, and can therefore adjudicate any use of it by derived classes. The state belongs to your class. Given that (in general) class closures are not repeatedly cloned, there's really little to choose from between "my" and "state" in the class definition itself.

    A given method may certainly declare a state variable and adjudicate its use as well. The state variable functions as a class variable that no other method can see, so it could be useful in the rare case that your method doesn't happen to trust the other methods in the class. Other than that, yes, a class attribute probably makes more sense.

      Could you also clarify the difference between state variables and local variables please? (Or have I missed the demise of the local declarator?)


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
        Well, technically, yes, you missed the demise of local, but only because we renamed it to temp and made it completely orthogonal to storage class declarations. You can temporize any variable, and it will restore itself at the end of the current lexical scope. You can also conditionally temporize a variable using let, and it resets at the block end only if the block fails. But these are operations on existing variables. Even in Perl 5, local never declares a variable, but merely temporarily changes the value of an existing global.

        And to reiterate, state variables are not globals. They're persistent lexicals. You can temporize them, but then you can temporize my variables as well. You can even temporize attribute accessors and not worry about whether the accessor represents a real attribute or is a wrapper around something else:

        temp $handle.autoflush = 1;
Re: How will you use state declared variables in Perl6?
by revdiablo (Prior) on Jul 08, 2005 at 22:28 UTC

    I really want to reply here, but I am having a hard time thinking of a good example. It's not that I wouldn't use state, but that I would use it so often and instinctively that I can't really come up with anything of note. It would be like asking me for a "good example" of using the assignment operator. It's the kind of subconscious thing where I just use it when I need it.

    That said, you [update: and mirod -- I must have missed his reply] already described the general circumstances when I would use state. Basically, any time I would reach for a bare-block-scoped lexical to create a closure. So instead of the Perl5esque:

    { my $foo; sub foo { $foo += @_; } }

    I'd use:

    sub foo { state $foo; $foo += @_; }

    Which -- while maybe not a big win -- I think is pretty nice.

    Update: BUU makes a good point.

      You say "maybe not a big win", but what about something like:
      foo(); { my $foo = 42; sub foo { print "Foo: $foo" } }
      Which I've done all too often.
Re: How will you use state declared variables in Perl6?
by theorbtwo (Prior) on Jul 09, 2005 at 04:39 UTC

    I think I'll likely use state vars mostly the same way I used the equiv p5 code: {my %foo; sub get_cached {my $x=shift; return $foo{$x} if exists $foo{$x}; $gotten = get($x); $foo{$x}=$gotten; return $gotten}.

    It's really quite useful when you want to create a quick-and-dirty cache without pulling in lots of dependencies you could easily avoid. Of course, complicate to taste with an expiration strategy, which I generally do in conjunction to cache loading.


    Warning: Unless otherwise stated, code is untested. Do not use without understanding. Code is posted in the hopes it is useful, but without warranty. All copyrights are relinquished into the public domain unless otherwise stated. I am not an angel. I am capable of error, and err on a fairly regular basis. If I made a mistake, please let me know (such as by replying to this node).

Re: How will you use state declared variables in Perl6?
by Juerd (Abbot) on Jul 10, 2005 at 12:19 UTC

    So how will you use state declared variables in Perl6?

    I would also use them with great caution, as I do with the Perl 5 closure equivalent. Named subs can become non-reentrant and impure. Currently, I think only caching of pure things is a good use, and for everything else, you should use some kind of instances. Since memoization will probably be available, probably not even for caching, will state be used much.

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

      Juerd,
      Since memoization will probably be available, probably not even for caching, will state be used much.

      I just added an example to pugs which uses state for memoization.

      use v6; my $n = @*ARGS[0] // 42; say fib($n); sub fib (Int $n) { state %seen; return 1 if $n < 2; %seen{$n - $_} //= fib($n - $_) for 1 .. 2; return %seen{$n - 1} + %seen{$n - 2}; }

      Cheers - L~R

        Hmm, why a hash? And why not optimize assuming the current value is already in @seen? Also, declarators need not be separate statements. So I'd probably write it like this:
        sub fib (Int $n) { return 1 if $n < 2; state @seen[$n] //= fib($n-1) + fib($n-2); }
        I think that may be a little clearer as well. If I wanted to golf it just a bit more I'd say:
        sub fib (Int $n) { $n < 2 or state @seen[$n] //= fib($n-1) + fib($n-2); }
        If I wanted to golf it a lot more I'd say:
        my&fib:={$_<2or state@s[$_]//=fib($_-1)+fib $_-2}

        My point was that you'd use standard memoization for that, not your own mechanism. In other words: state will (as far as I can estimate) not be used much for memoization, because it's easier to just say "is memoized" or something alike.

        Juerd # { site => 'juerd.nl', do_not_use => 'spamtrap', perl6_server => 'feather' }

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://473561]
Front-paged by kutsu
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (10)
As of 2014-04-17 22:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (458 votes), past polls