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 C<leave> :-)
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
> 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
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, *@data) {
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
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....
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
du...@lighthouse.tamucc.edu
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().
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 <C>&block .=
{ more_stuff(); };</C> is way up on my list...)
=Austin
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
But this is already supported, in its most powerful form:
wrap &block: { call; other_stuff() }
Luke
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 <C>infix:.=(Block, Block) {...}</C>.
Especially since the other way turns into:
macro atexit(Block $b) {
get_the_current_sub().eval("my &block = {};")
unless defined █
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 <C>eval()</C> pretty frequently. Better to provide some lower-level hackish way to agglutinate Blocks.
=Austin
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 █
> 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 <C>eval()</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
>
>
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.
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
> Austin_...@Yahoo.com (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
> 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'
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.
> Austin Hastings wrote:
> > But that imposes <C>eval()</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
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
d...@sidhe.org have teddy bears and even
teddy bears get drunk
Actually, that makes a good deal more sense than my suggestion. As
for hook functions, they probably want to hide themselves from
C<caller> anyway, or hooking a single function in a call chain could
cause introspective programs to fall over.
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 C<leave> :-)
:
: 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
We got rid of those; they're PMC poles now.
Get well soon,
Simon
--
"They laughed at Columbus, they laughed at Fulton, they laughed at the
Wright brothers. But they also laughed at Bozo the Clown."
-- Carl Sagan
Ditto!
Dave.
--
Little fly, thy summer's play my thoughtless hand has
terminated with extreme prejudice.
(with apologies to William Blake)