To demonstrate what I want, consider the following examples:
sub use_first()
{
for 1..2 {
FIRST {say 'entering loop';}
say $_;
LAST {say 'leaving loop';}
}
}
sub use_enter()
{
for 1..2 {
ENTER {say 'entering loop';}
say $_;
LAST {say 'leaving loop';}
}
}
The first time use_first is called it will print
entering loop
1
2
leaving loop
but subsequently it will print
1
2
leaving loop
and leave out the 'entering loop'.
When use_enter is called it will print
entering loop
1
entering loop
2
leaving loop
I want to output
entering loop
1
2
leaving loop
every time I run my loop. I suggest we create a new closure trait called
SETUP for this. Then the code could read
for 1..2 {
SETUP {say 'entering loop';}
say $_;
LAST {say 'leaving loop';}
}
Since SETUP can be used for initialization, it should probably be allowed
within an expression:
state $first_iteration = SETUP {true} will next {$_ = false};
Lower case 'setup' could probably also be used as a trait on a variable
state $first_iteration will setup {$_ = true} will next {$_ =
false};
Joe Gottman
JG> sub use_first()
JG> {
JG> for 1..2 {
JG> FIRST {say 'entering loop';}
JG> say $_;
JG> LAST {say 'leaving loop';}
JG> }
JG> }
JG> The first time use_first is called it will print
JG> entering loop
JG> 1
JG> 2
JG> leaving loop
JG> but subsequently it will print
JG> 1
JG> 2
JG> leaving loop
that seems to imply that FIRST is a program level first time action. i
think it does what you want and is executed on the first iteration of a
loop, each time the loop is started up. it is symmetrical to LAST in
that way. it should print the former text each time the sub is called.
but i could be wrong. :)
uri
--
Uri Guttman ------ u...@stemsystems.com -------- http://www.stemsystems.com
--Perl Consulting, Stem Development, Systems Architecture, Design and Coding-
Search or Offer Perl Jobs ---------------------------- http://jobs.perl.org
What's going on here is that the loop body is a closure that is
cloned upon entry to the loop (you're logically passing a closure
to the "for()" function that implements the loop), so if there's a
FIRST inside, it runs each time the loop is initialized. Likewise
state variables are logically regenerated for closure clones, so if
use_first() above wants to have a state variable that is maintained
from call to call, it must put it *outside* the loop.
Also, note that a LAST block, if any, has to be called from within
the implementation of for(), since only for() knows when it's done
with the loop as a whole.
It will be an interesting problem for the optimizer to figure out
how to avoid cloning closures that are passed only to synchronous
loop-controller functions such as for() and not otherwise used
asynchronously. Perhaps the signature of for() can make some
guarantees about not squirreling away the closure pointer in
some weird place. This is perhaps related to the issue of timely
GC on pointers you know haven't been copied into outside storage.
Larry
LW> : JG> The first time use_first is called it will print
LW> : JG> entering loop
LW> : JG> 1
LW> : JG> 2
LW> : JG> leaving loop
LW> :
LW> : JG> but subsequently it will print
LW> : JG> 1
LW> : JG> 2
LW> : JG> leaving loop
LW> :
LW> : that seems to imply that FIRST is a program level first time action. i
LW> : think it does what you want and is executed on the first iteration of a
LW> : loop, each time the loop is started up. it is symmetrical to LAST in
LW> : that way. it should print the former text each time the sub is called.
LW> What's going on here is that the loop body is a closure that is
LW> cloned upon entry to the loop (you're logically passing a closure
LW> to the "for()" function that implements the loop), so if there's a
LW> FIRST inside, it runs each time the loop is initialized. Likewise
LW> state variables are logically regenerated for closure clones, so if
LW> use_first() above wants to have a state variable that is maintained
LW> from call to call, it must put it *outside* the loop.
i am not clear on the actual answer. my take on what you said is that i
was right, FIRST will execute at the beginning of each time the loop is
entered which is what joe wants. am i correct?
LW> Also, note that a LAST block, if any, has to be called from within
LW> the implementation of for(), since only for() knows when it's done
LW> with the loop as a whole.
similarly for FIRST as only for() knows when it starts up the loop again
and reinitializes the counter.
LW> It will be an interesting problem for the optimizer to figure out
LW> how to avoid cloning closures that are passed only to synchronous
LW> loop-controller functions such as for() and not otherwise used
LW> asynchronously. Perhaps the signature of for() can make some
LW> guarantees about not squirreling away the closure pointer in
LW> some weird place. This is perhaps related to the issue of timely
LW> GC on pointers you know haven't been copied into outside storage.
i see the problem. if the loop body refers to lexicals declared outside
it, it looks like a classic perl5 closure and would need to be
cloned. what about the fact that for() will be called in effectively a
void context? a classic closure makes sense only when the code ref is
saved or possible called immediately via (p5) ->. for() is called
immediately but with a different signature as you said. the void context
would help the optimizer since you don't save the code block for later
reuse, no cloning should be done.
Yes.
: LW> Also, note that a LAST block, if any, has to be called from within
: LW> the implementation of for(), since only for() knows when it's done
: LW> with the loop as a whole.
:
: similarly for FIRST as only for() knows when it starts up the loop again
: and reinitializes the counter.
I think FIRST initialization happens automatically based on the cloning
mechanism, and the fact that FIRST knows when it's first already. The
code of a FIRST can actually be called inline, I think. Could be wrong
about that.
: LW> It will be an interesting problem for the optimizer to figure out
: LW> how to avoid cloning closures that are passed only to synchronous
: LW> loop-controller functions such as for() and not otherwise used
: LW> asynchronously. Perhaps the signature of for() can make some
: LW> guarantees about not squirreling away the closure pointer in
: LW> some weird place. This is perhaps related to the issue of timely
: LW> GC on pointers you know haven't been copied into outside storage.
:
: i see the problem. if the loop body refers to lexicals declared outside
: it, it looks like a classic perl5 closure and would need to be
: cloned. what about the fact that for() will be called in effectively a
: void context? a classic closure makes sense only when the code ref is
: saved or possible called immediately via (p5) ->. for() is called
: immediately but with a different signature as you said. the void context
: would help the optimizer since you don't save the code block for later
: reuse, no cloning should be done.
Doesn't really help--when you think about it, the block you pass to
map or grep is also a loop block, and those generally aren't used in
void context. It would have to be a marker on the actual block argument
in the signature. Maybe based on type:
sub mygrep (Block &block, *@list) {...} # don't clone
sub mysched (Closure &block, *@list) {...} # clone
Since the Block declaration would mostly be an optimizer hint,
I suspect the default should be to clone, so the latter can just
be written:
sub mysched (&block, *@list) {...} # clone
Actual type names are negotiable, of course. Maybe typename is the
wrong place for that info, and it should just be
sub mygrep (&block is uncloned, *@list) {...} # don't clone
Or maybe it's just spelled with existing props:
sub mygrep (&block is ref, *@list) {...} # don't clone
sub mygrep (&block is copy, *@list) {...} # clone (default)
Larry