List Info

Thread: state $foo = bar();




state $foo = bar();
user name
2007-09-04 14:08:54
Currently in 5.10nearly we have the keyword state, but no
STATE blocks,
explicit or implicit. So things like this

./perl -Ilib  -wlE 'sub bar ; sub baz {state $foo =
bar}; baz; baz'

assign to $foo, and hence call bar(), for each call of
baz():

Warning: something's wrong at -e line 1.
Warning: something's wrong at -e line 1.


As I understand it, in Perl 6, state $foo = bar; will
actually only assign
to $foo on the first execution of the statement, and
similarly only call bar()
once when it does it.

We had state assignment only once for a while, but Rafael
felt forced to
remove it in change 30149, I think because it always called
the RHS of the
expression. The appended patch solves that, *for scalar
assignment only*

So

$ ./perl -Ilib  -wlE 'sub bar ; sub baz {state $foo =
bar}; baz; baz'
Warning: something's wrong at -e line 1.


Or maybe more clearly:

$ ./perl -Ilib -lwE 'sub bar {warn "World!"; 4};
warn "Hello"; for (1..3) { state $a = bar; print
$a++; }'
Hello at -e line 1.
World! at -e line 1.
4
5
6


I'm using the same approach Rafael used until change 30149,
of hijacking
PADSTALE as the flag for whether a state variable has been
initialised yet.
(Well, to be fair, I retrieved his code from perforce and
used it)

Instead of putting a flag on the assignment op (sassign) I'm
shunting it and
its children underneath a new conditional op, tentatively
called "once",
which checks the PADSTALE flag on the SV of the state
variable - if it isn't
initialised, call the ops for the RHS, the LHS [padsv] and
then the assign.
If it is assigned, just call padsv to get the value. This
makes crazy code
like my $b = state $a = function(); work

The changes to t/op/state.t are mostly restoring Rafael's
changes to scalar
assignment of change 30149, plus the last line change to
reflect that
my $b = state $a = function(); now returns $a rather than
the value of
function() on the second and subsequent calls.


The optree looks something like this:

$ ./perl -Ilib -MO=Concise -lwE 'sub bar {warn
"World!"; 4}; warn "Hello"; for (1..3) {
state $a = bar; print $a++; }'
s  <> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 43 -e:1) v:%,{ ->3
5     <> warn[t1] vK/1 ->6
3        <0> pushmark s ->4
4        <$> const[PV "Hello"] s ->5
6     <;> nextstate(main 45 -e:1) v:%,{ ->7
r     <2> leaveloop vK/2 ->s
b        <{> enteriter(next->o last->r
redo->c) lKS/8 ->p
-           <0> ex-pushmark s ->7
-           <1> ex-list lK ->a
7              <0> pushmark s ->8
8              <$> const[IV 1] s ->9
9              <$> const[IV 3] s ->a
a           <#> gv[*_] s ->b
-        <1> null vK/1 ->r
q           <|> and(other->c) vK/1 ->r
p              <0> iter s ->q
-              <> lineseq vKP ->-
c                 <;> nextstate(main 43 -e:1) v:%
->d
-                 <1> null vK/1 ->j
d                    <|> once(other->e)[$a:43,44]
vK/1 ->t
-                       <0> null s ->d
i                       <2> sassign KS/2 ->-
g                          <1> entersub[t6] sKS/TARG,1
->h
-                             <1> ex-list sK ->g
e                                <0> pushmark s
->f
-                                <1> ex-rv2cv sK/128
->-
f                                   <#> gv[*bar] s
->-
h                          <0> padsv[$a:43,44]
sRM*/LVINTRO,STATE ->i
t                       <0> padsv[$a:43,44] sRM*/STATE
->j
j                 <;> nextstate(main 44 -e:1) v:%
->k
n                 <> print vK ->o
k                    <0> pushmark s ->l
m                    <1> postinc[t7] sK/1 ->n
l                       <0> padsv[$a:43,44] sRM
->m
o                 <0> unstack v ->p
-e syntax OK


HELP WANTED: I'm not able to get it to deparse. I think all
it needs is to
deparse the true branch of ONCE, but if I try this:

sub pp_once {
    my ($self, $op) = _;
    my $cond = $op->first;
    my $true = $cond->sibling;
    return $self->deparse($true);
}

B:eparse
gets very upset:

While deparsing -e near line 1,
Null op in deparse at lib/B/Deparse.pm line 796
        B:eparse::
deparse('B:eparse=H
ASH(0x82e701c)', 'B::NULL=SCALAR(0x855037c)', 7) called at
lib/B/Deparse.pm line 1970
        B:eparse::
deparse_binop_left('B:eparse=H
ASH(0x82e701c)', 'B::BINOP=SCALAR(0x855e09c)',
'B::NULL=SCALAR(0x855037c)', 7) called at lib/B/Deparse.pm
line 2020
...


What is wrong?



I have an idea how to do list assignment, but I'm not sure
that it's as
simple (and therefore definitely is a 5.12 thing). Basically
take this optree:


$ ./perl -Ilib -MO=Concise -lwE 'state ($a, $b) = (c(),
d())'
e  <> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 42 -e:1) v:%,{ ->3
d     <2> aassign[t7] vKS/COMMON ->e
-        <1> ex-list lKP ->a
3           <0> pushmark s ->4
6           <1> entersub[t4] lKS/TARG,1 ->7
-              <1> ex-list lK ->6
4                 <0> pushmark s ->5
-                 <1> ex-rv2cv sK ->-
5                    <#> gv[*c] s/EARLYCV ->6
9           <1> entersub[t6] lKS/TARG,1 ->a
-              <1> ex-list lK ->9
7                 <0> pushmark s ->8
-                 <1> ex-rv2cv sK ->-
8                    <#> gv[*d] s/EARLYCV ->9
-        <1> ex-list lKPRM*/144 ->d
a           <0> pushmark sRM*/144 ->b
b           <0> padsv[$a:42,43] lRM*/LVINTRO,STATE
->c
c           <0> padsv[$b:42,43] lRM*/LVINTRO,STATE
->d
-e syntax OK

splice a new conditional between these two hooked to the
PADSTALE of anything.
This conditional checks but doesn't change the PADSTALE
state.
Call it 2'

2     <;> nextstate(main 42 -e:1) v:%,{ ->3
3           <0> pushmark s ->4

If there is no initialisation done yet, continue from 2' to
3. If
initialisation is done, jump from 2' to a.

splice another new conditional (probably the same once
operator) between c and
d. Call it c'.

c           <0> padsv[$b:42,43] lRM*/LVINTRO,STATE
->d
d     <2> aassign[t7] vKS/COMMON ->e

If there is no initialisation done yet, continue from c' to
d. If it is already
done, jump to e. This skips the initialisation, but leaves
all the current
values of the state variables on the stack.

But it's not perfect, as you can't tell th difference
between

    state ($a, $b) = (c(), d());

and

    (state $a, state $b) = (c(), d())

which is necessary, as the former should run c() and d() and
assign each time:

19:44 < nwc10> is        (state $foo, my $bar) =
baz();
19:44 < nwc10> a syntax error?
19:44 < TimToady_> no, but it'll rewrite $foo each
time through
19:44 < nwc10> ah OK
19:44 < TimToady_> it doesn't magically turn an outer
= into pseudoassign
19:44 < nwc10> (state $foo, state $bar) = baz();
19:44 < nwc10> runs once or runs each time through?
19:45 < TimToady_> each time
19:45 < nwc10> state ($foo, $bar) = baz();
19:45 < nwc10> is only once?
19:45 < TimToady_> once


Nicholas Clark

==== //depot/perl/op.c#949 - /home/nick/p4perl/perl/op.c
====
--- /tmp/tmp.84038.1	Tue Sep  4 19:12:37 2007
+++ /home/nick/p4perl/perl/op.c	Tue Sep  4 18:45:33 2007
 -6986,6
+6986,29  Perl_ck_sassign(pTHX_ OP *o)
 	    return kid;
 	}
     }
+    if (kid->op_sibling) {
+	OP *kkid = kid->op_sibling;
+	if (kkid->op_type == OP_PADSV
+		&& (kkid->op_private & OPpLVAL_INTRO)
+		&& SvPAD_STATE(*av_fetch(PL_comppad_name,
kkid->op_targ, FALSE))) {
+	    const PADOFFSET target = kkid->op_targ;
+	    OP *const other = newOP(OP_PADSV,
+				    kkid->op_flags
+				    | ((kkid->op_private & ~OPpLVAL_INTRO)
<< 8));
+	    OP *const first = newOP(OP_NULL, 0);
+	    OP *const nullop = newCONDOP(0, first, o, other);
+	    OP *const condop = first->op_next;
+	    /* hijacking PADSTALE for uninitialized state
variables */
+	    SvPADSTALE_on(PAD_SVl(target));
+
+	    condop->op_type = OP_ONCE;
+	    condop->op_ppaddr = PL_ppaddr[OP_ONCE];
+	    condop->op_targ = target;
+	    other->op_targ = target;
+
+	    return nullop;
+	}
+    }
     return o;
 }
 
==== //depot/perl/opcode.h#144 -
/home/nick/p4perl/perl/opcode.h ====
--- /tmp/tmp.84038.2	Tue Sep  4 19:12:37 2007
+++ /home/nick/p4perl/perl/opcode.h	Tue Sep  4 14:47:48
2007
 -393,6
+393,7  EXTCONST char* const PL_op_name[] = {
 	"getlogin",
 	"syscall",
 	"lock",
+	"once",
 	"custom",
 };
 #endif
 -761,6
+762,7  EXTCONST char* const PL_op_desc[] = {
 	"getlogin",
 	"syscall",
 	"lock",
+	"once",
 	"unknown custom operator",
 };
 #endif
 -1143,6
+1145,7  EXT Perl_ppaddr_t PL_ppaddr[] /* or perl
 	MEMBER_TO_FPTR(Perl_pp_getlogin),
 	MEMBER_TO_FPTR(Perl_pp_syscall),
 	MEMBER_TO_FPTR(Perl_pp_lock),
+	MEMBER_TO_FPTR(Perl_pp_once),
 	MEMBER_TO_FPTR(Perl_unimplemented_op),	/* Perl_pp_custom
*/
 }
 #endif
 -1522,6
+1525,7  EXT Perl_check_t PL_check[] /* or perlva
 	MEMBER_TO_FPTR(Perl_ck_null),	/* getlogin */
 	MEMBER_TO_FPTR(Perl_ck_fun),	/* syscall */
 	MEMBER_TO_FPTR(Perl_ck_rfun),	/* lock */
+	MEMBER_TO_FPTR(Perl_ck_null),	/* once */
 	MEMBER_TO_FPTR(Perl_ck_null),	/* custom */
 }
 #endif
 -1895,6
+1899,7  EXTCONST U32 PL_opargs[] = {
 	0x0000000c,	/* getlogin */
 	0x0004281d,	/* syscall */
 	0x0000f604,	/* lock */
+	0x00000600,	/* once */
 	0x00000000,	/* custom */
 };
 #endif
==== //depot/perl/opcode.pl#166 -
/home/nick/p4perl/perl/opcode.pl ====
--- /tmp/tmp.84038.3	Tue Sep  4 19:12:38 2007
+++ /home/nick/p4perl/perl/opcode.pl	Tue Sep  4 14:23:42
2007
 -1047,4
+1047,8  syscall		syscall			ck_fun		imst	S L
 # For multi-threading
 lock		lock			ck_rfun		s%	R
 
+# For state support
+
+once		once			ck_null		|	
+
 custom		unknown custom operator		ck_null		0
==== //depot/perl/opnames.h#24 -
/home/nick/p4perl/perl/opnames.h ====
--- /tmp/tmp.84038.4	Tue Sep  4 19:12:38 2007
+++ /home/nick/p4perl/perl/opnames.h	Tue Sep  4 14:47:48
2007
 -375,11
+375,12  typedef enum opcode {
 	OP_GETLOGIN,	/* 357 */
 	OP_SYSCALL,	/* 358 */
 	OP_LOCK,	/* 359 */
-	OP_CUSTOM,	/* 360 */
+	OP_ONCE,	/* 360 */
+	OP_CUSTOM,	/* 361 */
 	OP_max		
 } opcode;
 
-#define MAXO 361
+#define MAXO 362
 #define OP_phoney_INPUT_ONLY -1
 #define OP_phoney_OUTPUT_ONLY -2
 
==== //depot/perl/pp.c#597 - /home/nick/p4perl/perl/pp.c
====
--- /tmp/tmp.84038.5	Tue Sep  4 19:12:38 2007
+++ /home/nick/p4perl/perl/pp.c	Tue Sep  4 18:30:53 2007
 -4932,6
+4932,19  PP(pp_split)
     RETURN;
 }
 
+PP(pp_once)
+{
+    dSP;
+    SV *const sv = PAD_SVl(PL_op->op_targ);
+
+    if (SvPADSTALE(sv)) {
+	/* First time. */
+	SvPADSTALE_off(sv);
+	RETURNOP(cLOGOP->op_other);
+    }
+    RETURNOP(cLOGOP->op_next);
+}
+
 PP(pp_lock)
 {
     dVAR;
==== //depot/perl/pp.sym#36 - /home/nick/p4perl/perl/pp.sym
====
--- /tmp/tmp.84038.6	Tue Sep  4 19:12:38 2007
+++ /home/nick/p4perl/perl/pp.sym	Tue Sep  4 14:47:48 2007
 -404,5
+404,6  Perl_pp_egrent
 Perl_pp_getlogin
 Perl_pp_syscall
 Perl_pp_lock
+Perl_pp_once
 
 # ex: set ro:
==== //depot/perl/pp_proto.h#44 -
/home/nick/p4perl/perl/pp_proto.h ====
--- /tmp/tmp.84038.7	Tue Sep  4 19:12:38 2007
+++ /home/nick/p4perl/perl/pp_proto.h	Tue Sep  4 14:47:48
2007
 -405,5
+405,6  PERL_PPDEF(Perl_pp_egrent)
 PERL_PPDEF(Perl_pp_getlogin)
 PERL_PPDEF(Perl_pp_syscall)
 PERL_PPDEF(Perl_pp_lock)
+PERL_PPDEF(Perl_pp_once)
 
 /* ex: set ro: */
==== //depot/perl/t/op/state.t#13 -
/home/nick/p4perl/perl/t/op/state.t ====
--- /tmp/tmp.84038.8	Tue Sep  4 19:12:38 2007
+++ /home/nick/p4perl/perl/t/op/state.t	Tue Sep  4 17:24:19
2007
 -10,7
+10,7  BEGIN {
 use strict;
 use feature "state";
 
-plan tests => 37;
+plan tests => 38;
 
 ok( ! defined state $uninit, q(state vars are undef by
default) );
 
 -18,7
+18,7  ok( ! defined state $uninit, q(state var
 
 sub stateful {
     state $x;
-    state $y //= 1;
+    state $y = 1;
     my $z = 2;
     state ($t) //= 3;
     return ($x++, $y++, $z++, $t++);
 -45,9
+45,9  is( $t, 5, 'incremented state var, list 
 # in a nested block
 
 sub nesting {
-    state $foo //= 10;
+    state $foo = 10;
     my $t;
-    { state $bar //= 12; $t = ++$bar }
+    { state $bar = 12; $t = ++$bar }
     ++$foo;
     return ($foo, $t);
 }
 -83,7
+83,7  is( $f2->(), 2, 'generator 2 once more' 
     sub TIESCALAR {bless {}};
     sub FETCH { ++$fetchcount; 18 };
     tie my $y, "countfetches";
-    sub foo { state $x //= $y; $x++ }
+    sub foo { state $x = $y; $x++ }
     ::is( foo(), 18, "initialisation with tied
variable" );
     ::is( foo(), 19, "increments correctly" );
     ::is( foo(), 20, "increments correctly,
twice" );
 -94,7
+94,7  is( $f2->(), 2, 'generator 2 once more' 
 
 sub gen_cashier {
     my $amount = shift;
-    state $cash_in_store;
+    state $cash_in_store = 0;
     return {
 	add => sub { $cash_in_store += $amount },
 	del => sub { $cash_in_store -= $amount },
 -113,7
+113,7  sub stateless {
     ++$reinitme;
 }
 is( stateless(), 43, 'stateless function, first time' );
-is( stateless(), 43, 'stateless function, second time' );
+is( stateless(), 44, 'stateless function, second time' );
 
 # array state vars
 
 -157,3
+157,4  noseworth(2);
 sub pugnax { my $x = state $y = 42; $y++; $x; }
 
 is( pugnax(), 42, 'scalar state assignment return value'
);
+is( pugnax(), 43, 'scalar state assignment return value'
);

Re: state $foo = bar();
user name
2007-09-05 06:25:00
On Tue, Sep 04, 2007 at 08:08:54PM +0100, Nicholas Clark
wrote:

> HELP WANTED: I'm not able to get it to deparse. I think
all it needs is to
> deparse the true branch of ONCE, but if I try this:
> 
> sub pp_once {
>     my ($self, $op) = _;
>     my $cond = $op->first;
>     my $true = $cond->sibling;
>     return $self->deparse($true);
> }
> 
> B:eparse
gets very upset:
> 
> While deparsing -e near line 1,
> Null op in deparse at lib/B/Deparse.pm line 796
>         B:eparse::
deparse('B:eparse=H
ASH(0x82e701c)', 'B::NULL=SCALAR(0x855037c)', 7) called at
lib/B/Deparse.pm line 1970
>         B:eparse::
deparse_binop_left('B:eparse=H
ASH(0x82e701c)', 'B::BINOP=SCALAR(0x855e09c)',
'B::NULL=SCALAR(0x855037c)', 7) called at lib/B/Deparse.pm
line 2020
> ...
> 
> 
> What is wrong?

It seems that I also need this:

 -7984,6
+8007,7  Perl_peep(pTHX_ register OP *o)
        case OP_DORASSIGN:
        case OP_COND_EXPR:
        case OP_RANGE:
+       case OP_ONCE:
            while (cLOGOP->op_other->op_type ==
OP_NULL)
                cLOGOP->op_other =
cLOGOP->op_other->op_next;
            peep(cLOGOP->op_other); /* Recursive calls
are not replaced by fptr calls */


which makes the output of Concise look more sensible. (OP
next pointers don't
route via null ops). But Deparse isn't happy.

So more help welcome.

Nicholas Clark

Re: state $foo = bar();
user name
2007-09-05 06:48:39
On 04/09/07, Nicholas Clark <nickccl4.org> wrote:
> We had state assignment only once for a while, but
Rafael felt forced to
> remove it in change 30149, I think because it always
called the RHS of the
> expression. The appended patch solves that, *for scalar
assignment only*

(That's 31049, actually)
Your patch and your approach looks nice. But more tests are
probably needed.

IIRC, one of the reasons I withdrew my state assignment
implementation
was that the return value of state() was broken. (See change
30772).
It should be tested in various situations and contexts.

Maybe we should forbid the syntaxes
    state($x, $y) = initial_values;
    state y = initial_values;
before we know what to do with it.

I'll have to look at it closer for the Deparse problems.

Re: state $foo = bar();
user name
2007-09-05 08:44:27
On Wed, Sep 05, 2007 at 12:25:00PM +0100, Nicholas Clark
wrote:
> On Tue, Sep 04, 2007 at 08:08:54PM +0100, Nicholas
Clark wrote:
> 
> > HELP WANTED: I'm not able to get it to deparse. I
think all it needs is to
> > deparse the true branch of ONCE, but if I try
this:
> > 
> > sub pp_once {
> >     my ($self, $op) = _;
> >     my $cond = $op->first;
> >     my $true = $cond->sibling;
> >     return $self->deparse($true);
> > }
> > 
> > B:eparse
gets very upset:
> > 
> > While deparsing -e near line 1,
> > Null op in deparse at lib/B/Deparse.pm line 796
> >         B:eparse::
deparse('B:eparse=H
ASH(0x82e701c)', 'B::NULL=SCALAR(0x855037c)', 7) called at
lib/B/Deparse.pm line 1970
> >         B:eparse::
deparse_binop_left('B:eparse=H
ASH(0x82e701c)', 'B::BINOP=SCALAR(0x855e09c)',
'B::NULL=SCALAR(0x855037c)', 7) called at lib/B/Deparse.pm
line 2020
> > ...
> > 
> > 
> > What is wrong?
> 
> It seems that I also need this:
> 
>  -7984,6 +8007,7  Perl_peep(pTHX_ register
OP *o)
>         case OP_DORASSIGN:
>         case OP_COND_EXPR:
>         case OP_RANGE:
> +       case OP_ONCE:
>             while (cLOGOP->op_other->op_type ==
OP_NULL)
>                 cLOGOP->op_other =
cLOGOP->op_other->op_next;
>             peep(cLOGOP->op_other); /* Recursive
calls are not replaced by fptr calls */
> 
> 
> which makes the output of Concise look more sensible.
(OP next pointers don't
> route via null ops). But Deparse isn't happy.

It's something to do with op->last not being set on the
sassign in:

$ ./perl -Ilib -MO=Concise -lwE 'state $a = 2;'
7  <> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 42 -e:1) v:%,{ ->3
-     <1> null vK/1 ->7
3        <|> once(other->4)[$a:42,43] vK/1 ->8
-           <0> null s ->3
6           <2> sassign KS/2 ->7
4              <$> const[IV 2] s ->5
5              <0> padsv[$a:42,43] sRM*/LVINTRO,STATE
->6
8           <0> padsv[$a:42,43] sRM*/STATE ->7
-e syntax OK


I infer that Deparse is expecting it to be set to point to
the padsv at '5'
based on the analogous

$ ./perl -Ilib -MO=Concise -E '$$ ? my $a = 3 : my $a '
8  <> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 42 -e:1) v:%,{ ->3
-     <1> null vK/1 ->8
4        <|> cond_expr(other->5) vK/1 ->9
-           <1> ex-rv2sv sK/1 ->4
3              <#> gvsv[*$] s ->4
7           <2> sassign vKS/2 ->8
5              <$> const[IV 3] s ->6
6              <0> padsv[$a:42,43] sRM*/LVINTRO
->7
9           <0> padsv[$a:42,43] vM/LVINTRO ->8
-e syntax OK

which does Deparse

But I don't know why that isn't happening. Some call to
linklist needed in?

    if (kid->op_sibling) {
        OP *kkid = kid->op_sibling;
        if (kkid->op_type == OP_PADSV
                && (kkid->op_private &
OPpLVAL_INTRO)
                &&
SvPAD_STATE(*av_fetch(PL_comppad_name, kkid->op_targ,
FALSE))) {
            const PADOFFSET target = kkid->op_targ;
            OP *const other = newOP(OP_PADSV,
                                    kkid->op_flags
                                    | ((kkid->op_private
& ~OPpLVAL_INTRO) << 8));
            OP *const first = newOP(OP_NULL, 0);
            OP *const nullop = newCONDOP(0, first, o,
other);
            OP *const condop = first->op_next;
            /* hijacking PADSTALE for uninitialized state
variables */
            SvPADSTALE_on(PAD_SVl(target));

            condop->op_type = OP_ONCE;
            condop->op_ppaddr = PL_ppaddr[OP_ONCE];
            condop->op_targ = target;
            other->op_targ = target;

            return nullop;
        }
    }


Nicholas Clark

Re: state $foo = bar();
user name
2007-09-05 15:13:06
Will this DWIM?


  sub timer_factory(){
  # generate coderefs that return how many seconds
  # since the first time they are used
      sub {
              state $first_time = time;
              time - $first_time
     };
  };




-- 
I'm gonna buy me a Mercury
And cruise it up and down the road

Re: state $foo = bar();
user name
2007-09-05 16:16:39
On Wed, Sep 05, 2007 at 03:13:06PM -0500, David Nicol
wrote:
> Will this DWIM?
> 
> 
>   sub timer_factory(){
>   # generate coderefs that return how many seconds
>   # since the first time they are used
>       sub {
>               state $first_time = time;
>               time - $first_time
>      };
>   };

It didn't quite to what *I* thought it meant:

$ cat timefactory.pl
#!./perl -l
use feature 'state';

sub timer_factory(){
    # generate coderefs that return how many seconds
    # since the first time they are used
    sub {
        state $first_time = time;
        time - $first_time
    };
};                                                          
               

print scalar localtime;

my $tf1 = timer_factory;
sleep 3;

print scalar localtime;
print $tf1->();

my $tf2 = timer_factory;
sleep 5;

print scalar localtime;
print $tf1->();
print $tf2->();

sleep 1;

print scalar localtime;
print $tf1->();
print $tf2->();

print $tf1;
print $tf2;
__END__
$ ./perl -Ilib timefactory.pl
Wed Sep  5 22:04:04 2007
Wed Sep  5 22:04:07 2007
0
Wed Sep  5 22:04:12 2007
5
5
Wed Sep  5 22:04:13 2007
6
6
CODE(0x814f340)
CODE(0x814f340)


At first I thought that it wasn't doing what I thought
because it doesn't
start counting until the sub is called, but there's actually
something more
'fun' in that the state variable is shared between the
subroutines. I think
that this falls out of the way that closures are already
implemented.

The subroutine in your example doesn't appear to close on
anything, so (my
understanding is that) it doesn't actually become a closure.
For my variables
this wouldn't matter (I think)

But clearly it does become visible here. And it's nothing to
do with my
patch, because if I write it as

sub timer_factory(){
    # generate coderefs that return how many seconds
    # since the first time they are used
    sub {
        state $first_time //= time;
        time - $first_time
    };
};

and run it on an unpatched blead, I still see

Wed Sep  5 23:08:12 2007
Wed Sep  5 23:08:15 2007
0
Wed Sep  5 23:08:20 2007
5
5
Wed Sep  5 23:08:21 2007
6
6
CODE(0x82a5580)
CODE(0x82a5580)

[for anyone confused by times, the unpatched blead is on a
machine in France]


I think that Dave has explained why those aren't closures,
but I forget
exactly why. Something to do with efficiency, as cloning
subroutines is not
free? Whatever, I think it's the same reason that blessing
subroutine
references can get "tricky". (and not do what one
would expect)

Nicholas Clark

Re: state $foo = bar();
user name
2007-09-05 16:32:35
On Wed, September 5, 2007 2:16 pm, Nicholas Clark wrote:
> On Wed, Sep 05, 2007 at 03:13:06PM -0500, David Nicol
wrote:
>> sub timer_factory(){ # generate coderefs that
return how many seconds
>> # since the first time they are used
>> sub { state $first_time = time; time - $first_time
};
>> };
>>
>
> It didn't quite to what *I* thought it meant:
>
>
> $ cat timefactory.pl
> #!./perl -l
> use feature 'state';
>
> sub timer_factory(){ # generate coderefs that return
how many seconds
> # since the first time they are used
> sub { state $first_time = time; time - $first_time };
> };
>
>
> print scalar localtime;
>
> my $tf1 = timer_factory; sleep 3;
>
> print scalar localtime; print $tf1->();
>
> my $tf2 = timer_factory; sleep 5;
>
> print scalar localtime; print $tf1->(); print
$tf2->();
>
> sleep 1;
>
> print scalar localtime; print $tf1->(); print
$tf2->();
>
> print $tf1; print $tf2; __END__
> $ ./perl -Ilib timefactory.pl
> Wed Sep  5 22:04:04 2007
> Wed Sep  5 22:04:07 2007
> 0
> Wed Sep  5 22:04:12 2007
> 5
> 5
> Wed Sep  5 22:04:13 2007
> 6
> 6
> CODE(0x814f340)
> CODE(0x814f340)
>
>
>
> At first I thought that it wasn't doing what I thought
because it doesn't
>  start counting until the sub is called, but there's
actually something
> more 'fun' in that the state variable is shared between
the subroutines. I
> think that this falls out of the way that closures are
already
> implemented.

I bet if it were a closure it would work.  But it's not a
closure, so
doesn't get its own CV & pad (I think).  This could be
considered a bug
in how state interacts with non-closure anon subs.

> I think that Dave has explained why those aren't
closures, but I forget
> exactly why. Something to do with efficiency, as
cloning subroutines is
> not free? Whatever, I think it's the same reason that
blessing subroutine
> references can get "tricky". (and not do what
one would expect)

Given that we're introducing another problem with them,
maybe the cloning
should happen all the time now?  Blessed non-closure
anonymous subs are
something of an oddity, but David Nicol's usage above isn't
(well, except
for the prototype bit).



Re: state $foo = bar();
user name
2007-09-05 17:07:22
Yitzchak Scott-Thoennes wrote:
> On Wed, September 5, 2007 2:16 pm, Nicholas Clark
wrote:
>   
>> On Wed, Sep 05, 2007 at 03:13:06PM -0500, David
Nicol wrote:
>>     
>>> sub timer_factory(){ # generate coderefs that
return how many seconds
>>> # since the first time they are used
>>> sub { state $first_time = time; time -
$first_time };
>>> };
>>>
>>>       
>> It didn't quite to what *I* thought it meant:
>>
>>
>> $ cat timefactory.pl
>> #!./perl -l
>> use feature 'state';
>>
>> sub timer_factory(){ # generate coderefs that
return how many seconds
>> # since the first time they are used
>> sub { state $first_time = time; time - $first_time
};
>> };
>>
>>
>> print scalar localtime;
>>
>> my $tf1 = timer_factory; sleep 3;
>>
>> print scalar localtime; print $tf1->();
>>
>> my $tf2 = timer_factory; sleep 5;
>>
>> print scalar localtime; print $tf1->(); print
$tf2->();
>>
>> sleep 1;
>>
>> print scalar localtime; print $tf1->(); print
$tf2->();
>>
>> print $tf1; print $tf2; __END__
>> $ ./perl -Ilib timefactory.pl
>> Wed Sep  5 22:04:04 2007
>> Wed Sep  5 22:04:07 2007
>> 0
>> Wed Sep  5 22:04:12 2007
>> 5
>> 5
>> Wed Sep  5 22:04:13 2007
>> 6
>> 6
>> CODE(0x814f340)
>> CODE(0x814f340)
>>
>>
>>
>> At first I thought that it wasn't doing what I
thought because it doesn't
>>  start counting until the sub is called, but
there's actually something
>> more 'fun' in that the state variable is shared
between the subroutines. I
>> think that this falls out of the way that closures
are already
>> implemented.
>>     
>
> I bet if it were a closure it would work.  But it's not
a closure, so
> doesn't get its own CV & pad (I think).  This could
be considered a bug
> in how state interacts with non-closure anon subs.
>
>   
>> I think that Dave has explained why those aren't
closures, but I forget
>> exactly why. Something to do with efficiency, as
cloning subroutines is
>> not free? Whatever, I think it's the same reason
that blessing subroutine
>> references can get "tricky". (and not do
what one would expect)
>>     
>
> Given that we're introducing another problem with them,
maybe the cloning
> should happen all the time now?  Blessed non-closure
anonymous subs are
> something of an oddity, but David Nicol's usage above
isn't (well, except
> for the prototype bit).
>
>
>
>   
it sounds to me like state vars should be experimental.

Re: state $foo = bar();
user name
2007-09-05 18:10:10
On 9/5/07, Jim Cromie <jim.cromiegmail.com> wrote:
> it sounds to me like state vars should be
experimental.
>

sub counterfactory(){
      my $_force_closure = $_un::knowable;
      sub {
            my $initial if 0;
            $_side::effect = $_force_closure;
           ++$initial
}   }
$y =  counterfactory;
$x = counterfactory;
print $x->();print $x->();print $x->();print
$y->();print $x->();
print $y->();print $x->();print $y->();print
$x->();print $y->();
__END__
1231425364
works as expected;
without the closure-forcing,
sub counterfactory(){
  sub {
            my $initial if 0;
           ++$initial
}   }
$y =  counterfactory;
$x = counterfactory;
print $x->();print $x->();print $x->();print
$y->();print $x->();
print $y->();print $x->();print $y->();print
$x->();print $y->();
__END__
12345678910

it does not.

-- 
I'm gonna buy me a Mercury
And cruise it up and down the road

Re: state $foo = bar();
user name
2007-09-06 03:57:23
On Wed, Sep 05, 2007 at 02:32:35PM -0700, Yitzchak
Scott-Thoennes wrote:
> On Wed, September 5, 2007 2:16 pm, Nicholas Clark
wrote:
> > I think that Dave has explained why those aren't
closures, but I forget
> > exactly why. Something to do with efficiency, as
cloning subroutines is
> > not free? Whatever, I think it's the same reason
that blessing subroutine
> > references can get "tricky". (and not do
what one would expect)
> 
> Given that we're introducing another problem with them,
maybe the cloning
> should happen all the time now?  Blessed non-closure
anonymous subs are
> something of an oddity, but David Nicol's usage above
isn't (well, except
> for the prototype bit).


If an anonymous sub prototype doesn't refer to any outer
lexical vars,
then its CV and pad are shared between all clones of the
anonymous sub.
This is an efficiency thing:

with this hack the following code

    for (1..30_000_000) {
	sub {}->();
    }

is about 6 times faster than

    for (1..30_000_000) {
	my $x;
	sub { $x}->();
    }

Without the hack, the first example about would run about 5
times slower.

Having said that, its a horrible hack that screws up
blessing of anon
coderefs: since they all share the same CV, blessing one
blesses them
all.

And in the case of state, it is clearly *very* wrong. I
could fairly
easily force anon subs with state vars to always be cloned.
RGS, would you
like me to do this before 5.10?



-- 
Lady Nancy Astor: If you were my husband, I would flavour
your coffee
with poison.
Churchill: Madam - if I were your husband, I would drink
it.

[1-10] [11-15]

about | contact  Other archives ( Real Estate discussion Medical topics )