[% setvar title Subroutines: Co-routines %]

This file is part of the Perl 6 Archive

Note: these documents may be out of date. Do not use as reference!

To see what is currently happening visit http://www.perl6.org/

TITLE

Subroutines: Co-routines

VERSION

  Maintainer: Damian Conway <damian@conway.org>
  Date: 4 Aug 2000
  Last Modified: 18 Sep 2000
  Number: 31
  Version: 2
  Mailing List: perl6-language-subs@perl.org
  Status: Frozen

ABSTRACT

This RFC proposes the addition of a new function return command: yield. Unlike return, yield preserves the execution state of the subroutine in which it's called, allowing the execution to be resumed at the following statement, next time the subroutine is called. It is also proposed that yields may nest, to simplify the construction of recursive co-routines and iterators.

BACKGROUND

Normally, when a subroutine returns, its execution terminates and it final context is completely lost. The next time the subroutine is invoked, it recommences executing from its first statement.

In a coroutine, a value may be returned in such a way that the execution of the routine is suspended, along with all its local context. The next time the routine in invoked, its execution resumes from the statement after the previous point of return.

DESCRIPTION

It is proposed to add a new control statement to Perl: yield. A yield acts very much like a return in that it terminates execution of the enclosing subroutine and returns a value to its calling context. However, when a value is yield'ed, the subroutine's execution is suspended in such a way that it resumes from the following statement next time the subroutine is invoked.

Note that any subroutine containing a yield is implicitly a co-routine. There is no explicit keyword or attribute proposed.

Coroutines make it very easy to implement generic parameteric closures and iterators:

        package Tree;

        sub next_inorder ($self) {
                yield $self->{left}->next_inorder if $self->{left};
                yield $self;
                yield $self->{right}->next_inorder if $self->{right};
                return undef;
        }


        # and later...

                while (my $node = $root->next_inorder()) {
                        print $node->{data};
                }

Note that the above example implies (correctly) that yielding a result which itself was yielded leaves the suspended execution of the subroutine at the same yield statement (not the following statement). Furthermore, yielding an undef is a no-op (i.e. it doesn't cause the subroutine to return, but passes control to the next statement).

Note that the arguments of a co-routine are ignored when it is resumed. Hence:

        sub every_second ( @vals ) {
                yield (splice @vals, 0, 2)[0]  while @vals;
                return;
        }

Another interesting application is to make a map block a coroutine, to allow it to process hashes in a natural manner. For example, instead of writing:

	@newhash{map {transform_key($_)} keys %oldhash} =
		map {transform_val($_)} values %oldhash;

one could write:

	%newhash = map {yield transform_key($_); transform_val($_)} %oldhash;

This flattens the %oldhash to a sequence of key/value pairs. Then the first time the map block is called (i.e. on a key) it transforms the key and immediately yields it. On the second iteration of the map, the block resumes after the yield and transforms the value. That normal return resets the block so that for the next iteration (another key) it applies the key transform and yields, then tranforms the second value, etc., etc.

IMPLEMENTATION

Tricky.

REFERENCES

None.