Skip to content

Commit a332fbc

Browse files
committed
Spec "outcome/whenever" instead of "winner/done/more"
For discussion, and for a test implementation. Please comment / fire on it / shoot it down if you think you should. By no means do I feel this is the final thing. It's just that winner {} currently is *so* broken, we either need to fix that, or come up with something else. This is just my something else :-)
1 parent f136000 commit a332fbc

File tree

1 file changed

+70
-40
lines changed

1 file changed

+70
-40
lines changed

S17-concurrency.pod

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -379,45 +379,83 @@ result.
379379
my ($a, $b) = await $p1, $p2;
380380

381381
This simply calls C<result> on each of the C<Promise>s, so any exception will
382-
be thrown. There is also a C<winner> statement [keywords still negotiable]:
382+
be thrown.
383383

384-
winner * {
385-
done $p1 { say "First promise got a value" }
386-
done $p2 { say "Second promise got a value" }
384+
There is also an C<outcome> statement [keywords still negotiable]. In its
385+
simplest form it just takes a list of C<Promise>s and C<Channel>s and returns
386+
a lazy list of all the values returned by all Promises and Channels specified:
387+
388+
my @values = outcome @p, @c;
389+
390+
If a C<Promise> is broken, then the value returned is a C<Failure> object
391+
with the C<.excuse>. Please note that this simple form will block until all
392+
values have been found. You can also put this simple form in a loop for
393+
processing:
394+
395+
for outcome( @p, @c ) -> $value {
396+
# process $value
387397
}
388398

389-
That will invoke the closure associated with the first promise that
390-
produces a result, either kept or broken.
399+
in which case values will be processed as they become available.
391400

392-
It's possible to add a timer using the keyword C<wait> followed
393-
by the number of seconds to wait (which may be fractional). As a
394-
degenerate case, in order to avoid blocking at all you may use a
395-
C<wait 0>. The timeout is always checked last, to guarantee that
396-
the other entries are all tried at least once before timing out.
401+
For more control, one can also specify a closure, in which there is more
402+
control over how a C<Promise> and / or a C<Channel> are being processed.
403+
In such a case, the returned lazy list with values is generally not needed:
397404

398-
my $gotone = winner * {
399-
done $p1 { say "First promise got a value"; $p1 }
400-
done $p2 { say "Second promise got a value"; $p2 }
401-
wait 0 { say "Not done yet"; Nil }
405+
my @values;
406+
outcome {
407+
whenever $p1 { say "p1 fired"; @values.push: $_ }
408+
whenever @p,@c { @values.push: $_ }
402409
}
403410

404-
The construct as a whole returns the result of whichever block was selected.
411+
Please note that for C<Promise>s, the C<whenever> will only be called once.
412+
For C<Channel>s, it will be called as soon as a value is present on the
413+
Channel until the Channel is C<.closed>.
414+
415+
There is also a C<done> keyword that can be used to specify code to be run
416+
when a C<Channel> has closed:
405417

406-
It's also possible to process a variadic list of promises together,
407-
using generic code that works over some set of the promises (use C<*>
408-
to represent any of them). The index and promise are passed to the
409-
code as named arguments C<$:v> and <$:k> (possibly via priming if
410-
the code is instantiated ahead of time).
418+
outcome {
419+
...
420+
done @c { say "Channel $_ has closed" }
421+
}
422+
423+
It's possible to add a timer using the keyword C<wait> followed by the
424+
number of seconds to wait (which may be fractional) since the last value
425+
found by a C<whenever> has been seen. The timeout is always checked last,
426+
to guarantee that the other entries are all tried at least once before
427+
timing out.
411428

412-
winner * {
413-
done @promises { say "Promise $:k was kept, result was: ", $:v.result }
429+
outcome {
430+
...
431+
wait 5 { say "Not done yet" }
414432
}
415433

416-
In this case C<$:k> returns the index of the promise, base 0.
417-
Likewise C<$:v> returns the promise object itself. Conjecture: the result
418-
should be bound to C<$_> to make it work more like channels below.
419434

420-
[Conjecture: we should allow different cases for success or failure.]
435+
As a degenerate case, in order to avoid blocking at all you may use a
436+
C<wait 0>. Together with C<last>, this allows one to exit the C<outcome>
437+
prematurely whenever there is nothing happening:
438+
439+
loop {
440+
outcome {
441+
...
442+
wait 0 { say "Giving up for now"; last }
443+
}
444+
# do other stuff
445+
}
446+
447+
Please note that you will need to C<.redeem> any C<Promise> that has been
448+
kept or broken, to prevent it from being seen again in the next loop
449+
iteration. Something like this:
450+
451+
my @values;
452+
loop {
453+
outcome {
454+
whenever @p { $:promise.redeem; @values.push: $_ }
455+
wait 0 { say "Giving up for now"; last }
456+
}
457+
# do other stuff
458+
}
421459

422460
=head1 Channels
423461

@@ -453,22 +491,14 @@ calls to a channel return the same promise, not a new one.
453491
While C<receive> blocks until it can read, C<poll> takes a message from the
454492
channel if one is there or immediately returns C<Nil> if nothing is there.
455493

456-
The C<winner> construct also works on channels, and will try to receive a value
457-
from the first C<Channel> that has one available. It also automatically checks
458-
the C<.done> promise corresponding to the channel, so it can also be used in order
459-
to write a loop to receive from a channel until it is closed:
494+
The C<outcome> construct also works on channels, and will try to receive
495+
values from the any C<Channel> that has value(s) available until they are
496+
all closed.
460497

461-
gather loop {
462-
winner $channel {
463-
more * { take $_ }
464-
done * { last }
465-
}
498+
outcome {
499+
whenever $channel { take $_ }
466500
}
467501

468-
This works because C<more> only ever works on channels, while C<done>
469-
only ever works on promises, so it knows to check the promise of
470-
channel C<$c> rather than C<$c> itself.
471-
472502
This is such a common pattern that we make a channel in list context behave
473503
that way:
474504

0 commit comments

Comments
 (0)