Closure trait for loop entry
Often when I write a loop I want to run some code at loop entry time. It would be nice to have a closure trait for this, similar to NEXT for loop continuation or LAST for loop termination, but there isn't one. I don't think either FIRST or ENTER do quite what I want. FIRST runs only once, which is too few times, and ENTER runs every time the loop block is entered, which is too many. 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
Re: Closure trait for loop entry
JG == Joe Gottman [EMAIL PROTECTED] writes: JGsub 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 -- [EMAIL PROTECTED] http://www.stemsystems.com --Perl Consulting, Stem Development, Systems Architecture, Design and Coding- Search or Offer Perl Jobs http://jobs.perl.org
Re: Closure trait for loop entry
On Sat, Feb 12, 2005 at 12:44:05PM -0500, Uri Guttman wrote: : JG == Joe Gottman [EMAIL PROTECTED] writes: : : JGsub 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. :) 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
Re: Closure trait for loop entry
LW == Larry Wall [EMAIL PROTECTED] writes: 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. uri -- Uri Guttman -- [EMAIL PROTECTED] http://www.stemsystems.com --Perl Consulting, Stem Development, Systems Architecture, Design and Coding- Search or Offer Perl Jobs http://jobs.perl.org
Re: Closure trait for loop entry
On Sat, Feb 12, 2005 at 03:55:40PM -0500, Uri Guttman wrote: : 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? 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, [EMAIL PROTECTED]) {...} # don't clone sub mysched (Closure block, [EMAIL PROTECTED]) {...} # 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, [EMAIL PROTECTED]) {...} # 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, [EMAIL PROTECTED]) {...}# don't clone Or maybe it's just spelled with existing props: sub mygrep (block is ref, [EMAIL PROTECTED]) {...} # don't clone sub mygrep (block is copy, [EMAIL PROTECTED]) {...}# clone (default) Larry