Re: The Block Returns
On Thu, Oct 16, 2003 at 01:46:30AM +0100, Simon Cozens wrote: [EMAIL PROTECTED] (Larry Wall) writes: But for the time being I'm tied to an IV pole We got rid of those; they're PMC poles now. Get well soon, Ditto! Dave. -- Little fly, thy summer's play my thoughtless hand has terminated with extreme prejudice. (with apologies to William Blake)
Re: The Block Returns
On Thu, Oct 02, 2003 at 01:59:26AM -0600, Luke Palmer wrote: : So, I must ask, what does this do: : : sub foo() { : return my $self = { : print Block; : return $self; : } : } : : my $block = foo; : print Main; : $block(); : print End; : : That is, the block returns from a function that's not currently : executing. : : Will the output be: a) : : Can't 'return' from closure Block It can't in general be determined at compile time whether the dynamic scope contains the original sub entry, so this one won't fly. Note also that a given return does not just return from a particular subroutine, but a particular subroutine invocation. When the closure is generated, the return throws a control exception that is specific about which invocation of foo() is to be returned from. : b) : : Main : Block : Can't 'return' from closure Block : (just like (a) but runtime) And if that particular invocation is not in the outer dynamic scope, it fails just like any uncaught exception, so I expect this is close to the correct answer. The whole point of doing this with control exceptions is so that we can say things like sub foo() { bar { return } } and know that the return is going to return from the current sub, even if the closure being passed to bar() is interpreted off somewhere else. So return is an invariant regardless of whether bar() is a built-in or not. : c) : : Main : Block : : (the block's return returns from the main program, or whatever function : is currently executing) To be consistent, that would return from bar() rather than foo() in my previous example. That would not be what we want return to do. : d) : : Main : Block : Main : Block : Main : Block : ... : : (the block closes over the function's return continuation) : : (a) and (b) both sound pretty good. (c) is a very bad idea, as it's : very subtle, breaks return type safety, introduces unexpected control : flow, etc. We'll leave those responsibilites to Cleave :-) : : Maybe (d) is the way we slip in continuations without anyone noticing. : It's the only one of these possibilites that works intuitively with, eg. : grep. It still seems like it might too easily introduce subtle bugs. If we force novices to think about continuations, we will have destroyed Perl as we know it. Continuations will be need to be carefully hidden in Perl 6, and available only by explicit request. : Is there another meaningful possibility that I didn't cover? I've heard : Smalltalk has something equivalent to the sub/block distinction; what : does it do? Dunno about Smalltalk. But I see sub/return as a *lexically* scoped contract to do what the user expects, even when that contract has to be enforced by run-time chicanery like control exceptions. By the way, sorry I haven't be more communicative lately, but I spent most of the last two months in the hospital, where there's no email access. Had a benign tumor chopped out of my stomach, and there were complications. Full recovery is expected, any month now. But for the time being I'm tied to an IV pole pumping calories into my intestines until I learn to eat again. So please don't expect me to come to any conferences any time soon... Larry
RE: The Block Returns
-Original Message- From: Luke Palmer [mailto:[EMAIL PROTECTED] Sent: Thursday, October 02, 2003 10:23 PM To: Jeff Clites Cc: [EMAIL PROTECTED]; [EMAIL PROTECTED] Subject: Re: The Block Returns Jeff Clites writes: Speaking to the practical side, I have written code that has to disentangle itself from the failure of a complex startup sequence. I'd love to be able to build a dynamic exit sequence. (In fact, being able to do Cblock .= { more_stuff(); };/C is way up on my list...) I've wanted to do that sort of thing before, but it seems simpler (conceptually and practically) to build up an array of cleanup subs/blocks to execute in sequence, rather than to have a .= for blocks. (Another reason it's handy to keep them separate is in cases in which each needs to return some information--maybe a status which determines whether to proceed, etc.) But this is already supported, in its most powerful form: wrap block: { call; other_stuff() } Hmm, no. That does a call, which presumes a return, which burns up who-knows-how-many mips. Given that the compiler is being forced to remember the code in case someone overloads the semicolon operator, or whatever, I don't think it's unreasonable to catenate the .source values of various blocks, and that seems a reasonable behavior for Cinfix:.=(Block, Block) {...}/C. Especially since the other way turns into: macro atexit(Block $b) { get_the_current_sub().eval(my block = {};) unless defined block; wrap block: { call; $b(); }; } Which makes two calls per additional whosit. Frankly, I think I'd rather see: macro atexit($code) is parsed(/{ Perl6.line* }/) { get_the_current_sub().eval(my $block;) unless defined $block; $block .= $code; } macro return($retval) { eval($block) if defined $block; leave Routine, $retval; } But that imposes Ceval()/C pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks. =Austin
Re: The Block Returns
Austin Hastings writes: -Original Message- From: Luke Palmer [mailto:[EMAIL PROTECTED] But this is already supported, in its most powerful form: wrap block: { call; other_stuff() } Hmm, no. That does a call, which presumes a return, which burns up who-knows-how-many mips. Given that the compiler is being forced to remember the code in case someone overloads the semicolon operator, or whatever, I don't think it's unreasonable to catenate the .source values of various blocks, and that seems a reasonable behavior for Cinfix:.=(Block, Block) {...}/C. Oh... so that's the reason. Well, you can give up hope on that, I'm pretty sure. If you want to do something like that, mess with the text and eval yourself. The reason is this: my code := { my $x = Hello; print $x; } { my $x = World; code ~= { print $x } } In any sane way of doing things, this should print HelloWorld. That means that your new appended block has to push the registers and load up a new scratchpad just like any closure would. The only thing you're saving is a jump to the proper address, which is mostly negligible. Especially since the other way turns into: macro atexit(Block $b) { get_the_current_sub().eval(my block = {};) unless defined block; wrap block: { call; $b(); }; } I really haven't the slightest idea what that's supposed to mean. Where'd block come from? What does Code.eval(String) mean? Which makes two calls per additional whosit. Frankly, I think I'd rather see: macro atexit($code) is parsed(/{ Perl6.line* }/) { get_the_current_sub().eval(my $block;) unless defined $block; $block .= $code; } macro return($retval) { eval($block) if defined $block; leave Routine, $retval; } But that imposes Ceval()/C pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks. And you suggest that this low-level hackish way is with a common operator, like C~. Oh, that's brilliant. Luke =Austin
Re: The Block Returns
[EMAIL PROTECTED] (Austin Hastings) writes: Frankly, I think I'd rather see: Some nits: macro atexit($code) is parsed(/{ Perl6.line* }/) { Probably just macro atexit($code) is parsed(/Perl6.block/) { $block .= $code; $block _= $code; Dunno what .= would mean now . is method call. I'm sure someone will make it mean something. :) eval($block) if defined $block; I prefer $block.compile.run to eval() -- The entirely inscrutable thing to me, looking back on myself, is my total want of all reason, will or design in the business; I had neither the resolution to win Adèle, the courage to do without her, the sense to consider what was at last to come of it all, or the grace to think how disagreeable I was making myself at the time to everybody about me. - John Ruskin.
Re: The Block Returns
Simon Cozens writes: [EMAIL PROTECTED] (Austin Hastings) writes: Frankly, I think I'd rather see: Some nits: macro atexit($code) is parsed(/{ Perl6.line* }/) { Probably just macro atexit($code) is parsed(/Perl6.block/) { $block .= $code; $block _= $code; $block ~= $code; Rather. :-) Dunno what .= would mean now . is method call. I'm sure someone will make it mean something. :) eval($block) if defined $block; I prefer $block.compile.run to eval() It'd might have to be, too, as $block isn't a string, it's a syntax tree (but in string context, it might just turn into a decompiled -- or saved -- form). Luke
Re: The Block Returns
On Fri, 3 Oct 2003, Simon Cozens wrote: [EMAIL PROTECTED] (Austin Hastings) writes: eval($block) if defined $block; I prefer $block.compile.run to eval() They're not quite equivalent -- I think eval's still wrapping a try/catch around the call. Dan
Re: The Block Returns
On Fri, 3 Oct 2003, Simon Cozens wrote: Dunno what .= would mean now . is method call. I'm sure someone will make it mean something. :) I've been saying for some time now that .= should mean exactly what one would expect it to mean, method call and assign the result, for code like $str .= lc; $linkedlist .= next; $structure .= clone(deep = 1); and such things. Really, making it mean anything else (including nothing at all) would be counterintuitive. -- Adam Lopresto http://cec.wustl.edu/~adam/ perl -le '$_=(split q,,,`$^Xdoc -q japh`)[1]..;y/pj/PJ/;print'
Re: The Block Returns
Austin Hastings wrote: -Original Message- From: Luke Palmer [mailto:[EMAIL PROTECTED] Sent: Thursday, October 02, 2003 10:23 PM To: Jeff Clites Cc: [EMAIL PROTECTED]; [EMAIL PROTECTED] Subject: Re: The Block Returns Jeff Clites writes: Speaking to the practical side, I have written code that has to disentangle itself from the failure of a complex startup sequence. I'd love to be able to build a dynamic exit sequence. (In fact, being able to do Cblock .= { more_stuff(); };/C is way up on my list...) I've wanted to do that sort of thing before, but it seems simpler (conceptually and practically) to build up an array of cleanup subs/blocks to execute in sequence, rather than to have a .= for blocks. (Another reason it's handy to keep them separate is in cases in which each needs to return some information--maybe a status which determines whether to proceed, etc.) But this is already supported, in its most powerful form: wrap block: { call; other_stuff() } Hmm, no. That does a call, which presumes a return, which burns up who-knows-how-many mips. Given that the compiler is being forced to remember the code in case someone overloads the semicolon operator, or whatever, I don't think it's unreasonable to catenate the .source values of various blocks, and that seems a reasonable behavior for Cinfix:.=(Block, Block) {...}/C. Especially since the other way turns into: macro atexit(Block $b) { get_the_current_sub().eval(my block = {};) unless defined block; wrap block: { call; $b(); }; } Which makes two calls per additional whosit. Frankly, I think I'd rather see: macro atexit($code) is parsed(/{ Perl6.line* }/) { get_the_current_sub().eval(my $block;) unless defined $block; $block .= $code; } macro return($retval) { eval($block) if defined $block; leave Routine, $retval; } But that imposes Ceval()/C pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks. Isn't this one of the prime examples of why CPS is being use, it allows for Tail Recursion Optimization. With TRO all your worries about overhead do to the wrap go away. -- [EMAIL PROTECTED] [EMAIL PROTECTED]
Re: The Block Returns
On Thu, 2 Oct 2003, Mark A. Biggar wrote: Austin Hastings wrote: But that imposes Ceval()/C pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks. Isn't this one of the prime examples of why CPS is being use, it allows for Tail Recursion Optimization. With TRO all your worries about overhead do to the wrap go away. This isn't why CPS is being used under the hood. (Nothing in perl 6 propmted CPS) I wouldn't necessarily count on being able to do tail calls here either, as they potentially alter the semantics, or at least the introspectable environment, of the program as they make frames go away. Dan
Re: The Block Returns
Dan Sugalski [EMAIL PROTECTED] writes: On Thu, 2 Oct 2003, Mark A. Biggar wrote: Austin Hastings wrote: But that imposes Ceval()/C pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks. Isn't this one of the prime examples of why CPS is being use, it allows for Tail Recursion Optimization. With TRO all your worries about overhead do to the wrap go away. This isn't why CPS is being used under the hood. (Nothing in perl 6 propmted CPS) I wouldn't necessarily count on being able to do tail calls here either, as they potentially alter the semantics, or at least the introspectable environment, of the program as they make frames go away. I'm vaguely hoping that modules will be able to declare that they don't need to access a 'strict' caller and that they'll be happy with a return from caller that skips any tail calls. Then, if the Perl 6 compiler sees that all modules in play have declared themselves in this fashion it'll use optimized tail calls. However, thinking about that I'm not entirely sure how it could be done with a single pass compiler.
Re: The Block Returns
At 11:55 PM +0100 10/3/03, Piers Cawley wrote: Dan Sugalski [EMAIL PROTECTED] writes: On Thu, 2 Oct 2003, Mark A. Biggar wrote: Austin Hastings wrote: But that imposes Ceval()/C pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks. Isn't this one of the prime examples of why CPS is being use, it allows for Tail Recursion Optimization. With TRO all your worries about overhead do to the wrap go away. This isn't why CPS is being used under the hood. (Nothing in perl 6 propmted CPS) I wouldn't necessarily count on being able to do tail calls here either, as they potentially alter the semantics, or at least the introspectable environment, of the program as they make frames go away. I'm vaguely hoping that modules will be able to declare that they don't need to access a 'strict' caller and that they'll be happy with a return from caller that skips any tail calls. Then, if the Perl 6 compiler sees that all modules in play have declared themselves in this fashion it'll use optimized tail calls. However, thinking about that I'm not entirely sure how it could be done with a single pass compiler. I think it's reasonable to allow you to mark your subs as being able to be tail-called out of. Subs with no lexical variables should be safe to call out of as well, though that's a bit dodgier. OTOH, it's not like too many folks walk up their call chain. -- Dan --it's like this--- Dan Sugalski even samurai [EMAIL PROTECTED] have teddy bears and even teddy bears get drunk
Re: The Block Returns
Dan Sugalski [EMAIL PROTECTED] writes: At 11:55 PM +0100 10/3/03, Piers Cawley wrote: Dan Sugalski [EMAIL PROTECTED] writes: On Thu, 2 Oct 2003, Mark A. Biggar wrote: Austin Hastings wrote: But that imposes Ceval()/C pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks. Isn't this one of the prime examples of why CPS is being use, it allows for Tail Recursion Optimization. With TRO all your worries about overhead do to the wrap go away. This isn't why CPS is being used under the hood. (Nothing in perl 6 propmted CPS) I wouldn't necessarily count on being able to do tail calls here either, as they potentially alter the semantics, or at least the introspectable environment, of the program as they make frames go away. I'm vaguely hoping that modules will be able to declare that they don't need to access a 'strict' caller and that they'll be happy with a return from caller that skips any tail calls. Then, if the Perl 6 compiler sees that all modules in play have declared themselves in this fashion it'll use optimized tail calls. However, thinking about that I'm not entirely sure how it could be done with a single pass compiler. I think it's reasonable to allow you to mark your subs as being able to be tail-called out of. Subs with no lexical variables should be safe to call out of as well, though that's a bit dodgier. OTOH, it's not like too many folks walk up their call chain. Actually, that makes a good deal more sense than my suggestion. As for hook functions, they probably want to hide themselves from Ccaller anyway, or hooking a single function in a call chain could cause introspective programs to fall over.
The Block Returns
So, I must ask, what does this do: sub foo() { return my $self = { print Block; return $self; } } my $block = foo; print Main; $block(); print End; That is, the block returns from a function that's not currently executing. Will the output be: a) Can't 'return' from closure Block b) Main Block Can't 'return' from closure Block (just like (a) but runtime) c) Main Block (the block's return returns from the main program, or whatever function is currently executing) d) Main Block Main Block Main Block ... (the block closes over the function's return continuation) (a) and (b) both sound pretty good. (c) is a very bad idea, as it's very subtle, breaks return type safety, introduces unexpected control flow, etc. We'll leave those responsibilites to Cleave :-) Maybe (d) is the way we slip in continuations without anyone noticing. It's the only one of these possibilites that works intuitively with, eg. grep. It still seems like it might too easily introduce subtle bugs. Is there another meaningful possibility that I didn't cover? I've heard Smalltalk has something equivalent to the sub/block distinction; what does it do? Luke
Re: The Block Returns
So, I must ask, what does this do: sub foo() { return my $self = { print Block; return $self; } } my $block = foo; # = sub {print Block; return $self;} A6: One obvious difference is that the sub on closures is now optional, since every brace-delimited block is now essentially a closure. You can still put the sub if you like. But it is only required if the block would otherwise be construed as a hash value; that is, if it appears to contain a list of pairs. print Main; $block(); print End; That is, the block returns from a function that's not currently executing. Main Block End is my guess. /Stefan
Re: The Block Returns
Stefan Lidman writes: So, I must ask, what does this do: sub foo() { return my $self = { print Block; return $self; } } my $block = foo; # = sub {print Block; return $self;} A6: One obvious difference is that the sub on closures is now optional, since every brace-delimited block is now essentially a closure. You can still put the sub if you like. But it is only required if the block would otherwise be construed as a hash value; that is, if it appears to contain a list of pairs. Um, yeah, but just a little farther down: Although we say the sub keyword is now optional on a closure, the return keyword only works with an explicit sub. (There are other ways to return values from a block.) And to clarify: sub indexof(Selector $which, [EMAIL PROTECTED]) { for zip(@data, 0...) - $_, $index { when $which { return $index } } } Which actually creates a closure (well, in theory at least) on line 2 for the for loop, but the return inside of it returns from indexof. Which is actually very, very nice. So the question is: What happens when indexof isn't on the call chain, but that inner closure is? Luke print Main; $block(); print End; That is, the block returns from a function that's not currently executing. Main Block End is my guess. /Stefan
Re: The Block Returns
On Thu, Oct 02, 2003 at 04:15:06AM -0600, Luke Palmer wrote: And to clarify: sub indexof(Selector $which, [EMAIL PROTECTED]) { for zip(@data, 0...) - $_, $index { when $which { return $index } } } Which actually creates a closure (well, in theory at least) on line 2 for the for loop, but the return inside of it returns from indexof. Which is actually very, very nice. So the question is: What happens when indexof isn't on the call chain, but that inner closure is? But how can the inner closure be called if not via indexof? -- To collect all the latest movies, simply place an unprotected ftp server on the Internet, and wait for the disk to fill
Re: The Block Returns
On Thu, Oct 02, 2003 at 11:39:20AM +0100, Dave Mitchell wrote: On Thu, Oct 02, 2003 at 04:15:06AM -0600, Luke Palmer wrote: So the question is: What happens when indexof isn't on the call chain, but that inner closure is? But how can the inner closure be called if not via indexof? I believe that's exactly what Luke's original example was illustrating. On Thu, Oct 02, 2003 at 01:59:26AM -0600, Luke Palmer wrote: So, I must ask, what does this do: sub foo() { return my $self = { print Block; return $self; } } foo() returns a closure that contains code that returns from the foo() subroutine (the line that says return $self) When that closure is then called ... my $block = foo; print Main; $block(); ... foo() is no longer executing. That is, the block returns from a function that's not currently executing. Will the output be: a) Can't 'return' from closure Block b) Main Block Can't 'return' from closure Block (just like (a) but runtime) c) Main Block (the block's return returns from the main program, or whatever function is currently executing) d) Main Block Main Block Main Block ... (the block closes over the function's return continuation) I would expect (a) to happen, but (d) has some interesting possibilities. And wouldn't (d) be: Main Block Block Block ... ? Actually, if your last parenthetical were true, it would be Main Block End because though foo()'s return continuation is closed over, it only gets executed once and then returned. I.e., to get Block again, you'd need to execute the return value of $block. my $block = foo; print Main; $b2 = $block(); $b3 = $b2(); $b4 = $b3();# etc. print End; or for the infinite version: my $block = foo; print Main; $block = $block() while 1; print End;# we never get here Or am I missing something? -Scott -- Jonathan Scott Duff [EMAIL PROTECTED]
Re: The Block Returns
Jonathan Scott Duff wrote: On Thu, Oct 02, 2003 at 11:39:20AM +0100, Dave Mitchell wrote: On Thu, Oct 02, 2003 at 04:15:06AM -0600, Luke Palmer wrote: So the question is: What happens when indexof isn't on the call chain, but that inner closure is? But how can the inner closure be called if not via indexof? I believe that's exactly what Luke's original example was illustrating. On Thu, Oct 02, 2003 at 01:59:26AM -0600, Luke Palmer wrote: So, I must ask, what does this do: sub foo() { return my $self = { print Block; return $self; } } foo() returns a closure that contains code that returns from the foo() subroutine (the line that says return $self) When that closure is then called ... my $block = foo; print Main; $block(); ... foo() is no longer executing. That is, the block returns from a function that's not currently executing. Will the output be: a) Can't 'return' from closure Block b) Main Block Can't 'return' from closure Block (just like (a) but runtime) c) Main Block (the block's return returns from the main program, or whatever function is currently executing) d) Main Block Main Block Main Block ... (the block closes over the function's return continuation) I would expect (a) to happen, but (d) has some interesting possibilities. And wouldn't (d) be: Main Block Block Block ... ? Actually, if your last parenthetical were true, it would be Main Block End because though foo()'s return continuation is closed over, it only gets executed once and then returned. I.e., to get Block again, you'd need to execute the return value of $block. my $block = foo; print Main; $b2 = $block(); $b3 = $b2(); $b4 = $b3();# etc. print End; or for the infinite version: my $block = foo; print Main; $block = $block() while 1; print End; # we never get here Or am I missing something? Possibly, I brought a similar example to Larry a while ago and he said that creating a closuer and assigning to a variable (or even returning it), may need to be an exception to the sub required for return rule, exactly because of this dangling context problem. So in the above example, the return is from the local closure not the from foo(). -- [EMAIL PROTECTED] [EMAIL PROTECTED]
RE: The Block Returns
-Original Message- From: Jonathan Scott Duff [mailto:[EMAIL PROTECTED] On Thu, Oct 02, 2003 at 11:39:20AM +0100, Dave Mitchell wrote: On Thu, Oct 02, 2003 at 04:15:06AM -0600, Luke Palmer wrote: So the question is: What happens when indexof isn't on the call chain, but that inner closure is? But how can the inner closure be called if not via indexof? I believe that's exactly what Luke's original example was illustrating. On Thu, Oct 02, 2003 at 01:59:26AM -0600, Luke Palmer wrote: So, I must ask, what does this do: sub foo() { return my $self = { print Block; return $self; } } foo() returns a closure that contains code that returns from the foo() subroutine (the line that says return $self) When that closure is then called ... my $block = foo; print Main; $block(); ... foo() is no longer executing. That is, the block returns from a function that's not currently executing. Will the output be: a) Can't 'return' from closure Block b) Main Block Can't 'return' from closure Block (just like (a) but runtime) c) Main Block (the block's return returns from the main program, or whatever function is currently executing) d) Main Block Main Block Main Block ... (the block closes over the function's return continuation) I would expect (a) to happen, but (d) has some interesting possibilities. I hope not. Otherwise the ability to use anonymous blocks would be severely impaired. If there's a list of keywords that can't be used in anonymous blocks, that's a pretty severe limitation. And really, is this any worse than an anonymous block that throws exceptions? Speaking to the practical side, I have written code that has to disentangle itself from the failure of a complex startup sequence. I'd love to be able to build a dynamic exit sequence. (In fact, being able to do Cblock .= { more_stuff(); };/C is way up on my list...) =Austin
Re: The Block Returns
Speaking to the practical side, I have written code that has to disentangle itself from the failure of a complex startup sequence. I'd love to be able to build a dynamic exit sequence. (In fact, being able to do Cblock .= { more_stuff(); };/C is way up on my list...) I've wanted to do that sort of thing before, but it seems simpler (conceptually and practically) to build up an array of cleanup subs/blocks to execute in sequence, rather than to have a .= for blocks. (Another reason it's handy to keep them separate is in cases in which each needs to return some information--maybe a status which determines whether to proceed, etc.) JEff
Re: The Block Returns
Jeff Clites writes: Speaking to the practical side, I have written code that has to disentangle itself from the failure of a complex startup sequence. I'd love to be able to build a dynamic exit sequence. (In fact, being able to do Cblock .= { more_stuff(); };/C is way up on my list...) I've wanted to do that sort of thing before, but it seems simpler (conceptually and practically) to build up an array of cleanup subs/blocks to execute in sequence, rather than to have a .= for blocks. (Another reason it's handy to keep them separate is in cases in which each needs to return some information--maybe a status which determines whether to proceed, etc.) But this is already supported, in its most powerful form: wrap block: { call; other_stuff() } Luke