Skip to content

Commit

Permalink
[S04] bring gather/take description more in line with modern realities
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.pugscode.org/pugs@29760 c213334d-75ef-0310-aa23-eaa082d1ae64
  • Loading branch information
lwall committed Feb 17, 2010
1 parent 07265d7 commit 7ee75f6
Showing 1 changed file with 42 additions and 30 deletions.
72 changes: 42 additions & 30 deletions S04-control.pod
Expand Up @@ -13,8 +13,8 @@ Synopsis 4: Blocks and Statements

Created: 19 Aug 2004

Last Modified: 17 Dec 2009
Version: 93
Last Modified: 16 Feb 2010
Version: 94

This document summarizes Apocalypse 4, which covers the block and
statement syntax of Perl.
Expand Down Expand Up @@ -683,28 +683,40 @@ followed by a statement modifier:
=head2 The C<gather> statement prefix
X<gather>X<take>

A variant of C<do> is C<gather>. Like C<do>, it is followed by a
statement or block, and executes it once. Unlike C<do>, it evaluates
the statement or block in sink (void) context; its return value is instead
specified by calling the C<take> list prefix operator one or more times
within the dynamic scope of the C<gather>. The C<take> function's
signature is like that of C<return>; it merely wraps up a C<Parcel>
of its arguments without imposing any additional constraints (in the
absence of context propagation by the optimizer). The value returned
by the C<take> to its own context is that same C<Parcel> object (which
is ignored when the C<take> is in sink context). Regardless of the
C<take>'s context, the C<Parcel> object is also added to the list of
values being gathered, which is returned by the C<gather> in the form
of a lazy list of C<Parcel>s, with each element corresponding to one C<take>
parcel. (A C<Parcel>s is normally flattened when bound into slurpy context,
but when bound into a slice context, the C<Parcel> objects become real
sublists. The eventual binding context thus determines whether to
throw away or keep the groupings resulting from each individual C<take> call.)
A variant of C<do> is C<gather>. Like C<do>, it is followed by a statement
or block, and executes it once. Unlike C<do>, it evaluates the statement or
block in sink (void) context; its return value is instead specified by calling
the C<take> list prefix operator one or more times within the dynamic scope of
the C<gather>. The C<take> function's signature is like that of C<return>;
while having the syntax of a list operator, it merely returns a single item
which if, if you return multiple items in a comma list, be wrapped up in a
C<Parcel> object. No additional constraints are enforce by context, since
all context is lazy in Perl 6. The flattening or non-flattening of any such
returned C<Parcel> will be dependent on how the gather's return iterator is
iterated (with .get vs .getarg).

The value returned by the C<take> to the C<take>'s own context is
that same returned object (which is ignored when the C<take> is in
sink context). Regardless of the C<take>'s immediate context, the
object returned is also added to the list of values being gathered,
which is returned by the C<gather> in the form of a lazy list (that
is, an iterator, really), with each element of that list corresponding
to one C<take> object (either a C<Parcel> or any other single object
serving as a degenerate parcel).

Any C<Parcel>s in the returned list are normally flattened when bound
into slurpy context. When bound into a slice context, however,
the C<Parcel> objects become real C<Seq> objects that keep their
identity as discrete sublists. The eventual binding context thus
determines whether to throw away or keep the groupings resulting from
each individual C<take> call. Since most list contexts are flat
rather than sliced, the boundaries between individual C<take>
calls usually disappear.

Because C<gather> evaluates its block or statement in sink context,
this typically causes the C<take> function to be evaluated in sink
context. However, a C<take> function that is not in sink context
gathers its arguments I<en passant> and also returns them unchanged.
gathers its return objects I<en passant> and also returns them unchanged.
This makes it easy to keep track of what you last "took":

my @uniq = gather for @list {
Expand All @@ -720,24 +732,24 @@ may bind or coerce the resulting parcels differently:

my @y;
@x = gather for 1..2 { # flat context for list of parcels
my $x = take $_, $_ * 10; # item context for individual parcel
push @y, $x;
my ($y) := take $_, $_ * 10; # item context promotes parcel to seq
push @y, $y;
}
# @x contains 4 Ints: 1,10,2,20
# @y contains 2 Parcels: (1,10),(2,20)
# @x contains 4 Ints: 1,10,2,20 flattened by list assignment to @x
# @y contains 2 Seqs: Seq(1,10),Seq(2,20) sliced by binding to positional $y

Likewise, we can just remember the gather's result by binding and
later coerce it:
Likewise, we can just remember the gather's result parcel by binding and
later coercing it:

$c := gather for 1..2 {
my |$c := gather for 1..2 {
take $_, $_ * 10;
}
# $c.flat produces 1,10,2,20 -- flatten fully into a list of Ints.
# $c.slice produces (1,10),(2,20) -- list of Parcels, a 2-D list.
# $c.item produces ((1,10),(2,20)) -- the saved Parcel itself as one item in item context.
# $c.slice produces Seq(1,10),Seq(2,20) -- list of Seqs, a 2-D list.
# $c.item produces Seq((1,10),(2,20)) -- coerced to Seq of unresolved Parcels

Note that the C<take> itself is in sink context in this example because
the C<for> loop is in sink context.
the C<for> loop is in the sink context provided inside the gather.

A C<gather> is not considered a loop, but it is easy to combine with a loop
statement as in the examples above.
Expand Down

0 comments on commit 7ee75f6

Please sign in to comment.