Front page | perl.perl6.language |
Postings from May 2003
Re: Coroutines
Thread Previous
|
Thread Next
From:
Luke Palmer
Date:
May 22, 2003 18:19
Subject:
Re: Coroutines
Message ID:
ygc1xyqtq7t.fsf@babylonia.flatirons.org
Damian wrotes:
> Didn't we already have this discussion six months ago???
> The conclusion of which seemed to be:
>
> http://archive.develooper.com/perl6-language@perl.org/msg12518.html
>
> However, on revisiting it, I think I have an even simpler, best-of-both-worlds
> solution that's also more in line with A6...
>
> ==============================================================================
>
> A coroutine is declared with the C<coro> declarator:
>
> coro fibs (?$a = 0, ?$b = 1) {
> loop {
> yield $b;
> ($a, $b) = ($b, $a+$b);
> }
> }
>
> and is allowed to have zero or more C<yield> statements inside it.
>
> A coroutine is an object of type C<Coro> (just as a subroutine is an object of
> type C<Sub>). C<Coro> objects have the following methods:
>
> next() # resumes coroutine body until next C<yield>
>
> next_args(PARAM_LIST) # resumes coroutine body until next C<yield>,
> # rebinds params to the args passed to
> # C<next_args>.
> # PARAM_LIST is the same as the parameter list
> # of the coroutine itself
>
> each() # returns a lazy array, each element of which
> # is computed on demand by the appropriate
> # number of resumptions of the coroutine body
>
> A C<Coro> object is therefore a type of iterator.
>
> Calling a coroutine using the normal subroutine call syntax is the same as
> calling the C<next> or C<next_args> method on it:
>
> $next = fibs(); # same as: $next = &fibs.next()
>
> $next = fibs(10,11); # same as: $next = &fibs.next_args(10,11)
>
> while ($next = fibs()) {
> if ($next < 0) {
> fibs() for 1..2; # skip the next two values
> }
> }
>
>
> To create multiple independent coroutine interators using a single coroutine
> definition, one can simply use an *anonymous* coroutine:
>
> sub fibs_iter (?$a = 0, ?$b = 1){
> return coro (?$x = $a, ?$y = $b) {
> loop {
> yield $b;
> ($a, $b) = ($b, $a+$b);
> }
> }
> }
>
> # and later...
>
> $iter = fibs_iter(7,11);
>
> while ($next = $iter.next()) {
> if ($next < 0) {
> $iter.next() for 1..2; # skip the next two values...
> $iter = fibs_iter(11,7); # and change iterator
> }
> }
>
> Additionally, class C<Coro> would have a C<clone> method:
>
> $iter1 = &fibs.clone;
> $iter2 = &fibs.clone;
>
> which would return a reference to a new anonymous C<Coro> with the same
> implementation as (but distinct state from) the original C<Coro>.
Sweet.
> Either way, for iteration that implies:
>
> <$fh> # Call $fh.next() # IO objects are iterators too
> <$iter> # Call $iter.next()
> coro() # Call &coro.next()
> <&coro> # Call &coro.next()
> <coro()> # Call .next() on iterator returned by call to coro()
>
>
> Whilst, in a list context:
>
> <$fh> # Call $fh.each()
> <$iter> # Call $iter.each()
> coro() # Call &coro.each()
Er, what if it wanted to yield a list?
> <&coro> # Call &coro.each()
> <coro()> # Call .each() on iterator returned by call to coro()
>
>
> So then:
>
> for <$fh> {...} # Build and then iterate a lazy array (the elements
> # of which call back to the filehandle's input
> # retrieval coroutine)
>
> for <$iter> {...} # Build and then iterate a lazy array (the elements
> # of which call back to the iterator's coroutine)
>
> for coro() {...} # Build and then iterate a lazy array (the elements
> # of which call back to the C<&coro> coroutine)
>
> for <&coro> {...} # Build and then iterate a lazy array (the elements
> # of which call back to the C<&coro> coroutine)
>
> for <coro()> {...} # Call coro(), then build and iterate a lazy
> # array (the elements of which call back to the
> # iterator returned by the call to C<coro()>)
>
>
> ==============================================================================
>
> This approach preserves the simple (and I suspect commonest) use of "monadic"
> coroutines, but still allows a single coroutine to produce multiple iterators.
> It also minimizes additional syntax and semantics.
I like most of it. There's one problem: recursion.
coro count($n) {
count($n - 1) if $n > 0;
yield $n;
}
The call to count() inside count() would either start a new coroutine,
or continue the old one -- neither of which are the desired behavior.
What you want in this case is just to call the sub as usual. This can
be remedied like this:
sub _count_impl($n) {
_count_impl($n - 1) if $n > 0;
yield $n;
}
coro count($n) {
_count_impl($n);
}
But that seems like an awful lot of work for something this common.
Hmm...
coro count($n) {
(sub { _($n - 1) if $n > 0; yield $n }).($n);
}
Cool, but, eew.
I'm not sure how to solve the recursion problem while appeasing those
who want implicit iteration.
Another detail: is implicit iteration scoped or global?
Luke
Thread Previous
|
Thread Next