Closure trait for loop entry

2005-02-12 Thread Joe Gottman
   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

2005-02-12 Thread Uri Guttman
 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

2005-02-12 Thread Larry Wall
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

2005-02-12 Thread Uri Guttman
 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

2005-02-12 Thread Larry Wall
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