Synopsis 8: Capture and Parcel


    Daniel Ruoso <daniel@ruoso.com>


    Created: 20 Sep 2009

    Last Modified: 23 Jan 2010
    Version: 2


Unlike most programming languages, the data structure that is used to send the parameters into a routine invocation (be it a method or a sub) is exposed to the language as a built-in type like any other. This represents a very important aspect of the Perl 6 runtime requirements.

Additionally to the fact that this data structure is visible in the language type system, it is not assumed that the capture is a native type, which means that the internal representation of the data is subject to change. The only thing the runtime can assume is the API described here.

Of course the runtime can cheat whenever it knows the capture is implemented by its own internal data structure for optimization purposes, but it should allow the use of foreign types when invoking a routine, as long as the object says true to .^does(Capture).

Captures and Parcels are also the basis for the multidimensionality of lists in Perl 6. Unlike Perl 5, no flattening happens unless it's explicitly required by the user, which is done by enforcing the list context. If you use the item context the dimensionality should be preserved.

In addition to the list and item context, there's also a special context, which is, in a simplified way, called "Capture context", but it actually means "deferred context", in a way that the Capture or Parcel is kept as-is while they are manipulated in the code. This is useful to avoid unwanted flattening as well as avoiding the DWIMmy features that might change the capture's behavior.

This is the main point of why Capture and Parcel replace Perl 5 references, they allow you to send data untouched from one place to another. The second reason is that as in Perl 6 everything is an object, there isn't really "pass-by-value" anymore, you're always sending a reference, Captures and Parcels simply carry other objects without enforcing any context on them.

Capture or Parcel

While a Capture is the object that holds the parameters sent to a routine (positional and named), a Parcel is a more fundamental data structure that doesn't really differentiate named arguments from positional arguments (but it still lets you access the named ones by their name).

The basic underlying concept is that a Parcel behaves much like a list, but it doesn't enforce any context, in a way that no flattening or coercion is done. When you use the Positional API on a Parcel, it will include all the listed items, whether they look like named arguments or positional arguments. For example:

  1, 2, :a<b>

The Parcel represented here has 3 positional items and allows you to access the element 'a' through the Associative interface. A Parcel might be statically converted to a Capture if it's clear to the parser that it's being used as the arguments to a routine call.

A Capture, on the other hand, is not required to keep the positional information for the named arguments, for example:


In the call to the routine foo, there are only two positional arguments and one named argument, and you won't be able to find "b" from the Positional interface, but only from the Associative.

The differentiation from Parcel and Capture is important to keep the regular use of inline declarations consistent, let's say you do the following:

  my $a = (0, :a<b>, 2);
  say $a[2];

If we had Capture and Parcel as the same data structure, you wouldn't get 2 as the result of the above code, because there are only two positional arguments, not three. Using the same example:

  sub foo($p1, $p2, :$a) {...}

In that case, the Parcel is converted into a Capture, and therefore the pair :a<b> is no longer visible as a positional argument, only as named.

Note that once you convert a Parcel into a Capture, you won't be able to get the original Parcel again, because a Capture doesn't hold the information about the position of named arguments.


Probably the most important task of Parcels and Captures is to implement the multidimensionality of lists in Perl 6, this means that the barrier used to detect the dimensionality of the data structures by the operators is whatever the item inside it implements Parcel or Capture. For instance:

  my $a = (1, (2, (3, 4)));
  say $a[1];

In that case, you'll get 2, (3, 4) (or whatever is implemented in the .Str method of that specific Parcel).

[Update: The above (and much of what follows below) doesn't conform to the latest discussion on parcels; parcels flatten under item assignment. See http://irclog.perlgeek.de/perl6/2009-09-23#i_1532822.]

But, you should be able to:

  say $a[1;0];

Which is going to return 2, which is almost the same as:

  say $a[1][0];

But the first provides a more convenient and optimizeable way of asking for it. If you want to get the value 4 from that data structure you need to:

  say $a[1;1;1];

Note that if you assign that parcel to an array, it will be flattened, so:

  my @a = 1, (2, (3, 4));
  say @a[3];

Would print 4, at the same time that trying to ask for multidimensionality information from that list would result in a failure:

  say @a[1;1;1];

As the element 1 of the array @a is not a Capture or a Parcel, it is not possible for the .[] operator to traverse it.

[Conjecture: It is still not clear if the multidimensional access should be able to get into regular arrays, i.e.: [1,[2,[3,[4]]]] ]

It is important to realize that it's not the parens that are creating the Parcel, but the infix:<,>. The parens are only required in order to define a sub-parcel.

On the other hand, if you bind a parcel to a variable, it doesn't really matter which sigil it uses:

  my @a := (1, (2, (3, 4)));
  say @a[1;1;1]; # "4"
  say @a[3]; # failure

Captures and Parcels are seen the same way regarding multidimensionality, for instance:

  my $a = ((map { $_ * 2 }, 1..5),(map { $_ / 2 }, 1..5));
  say $a[0;0]; # 2
  say $a[1;0]; # 0.5

The same way, if each map closure returns more than one item inside its capture:

  my $a = ((map { $_ * 2, $_ / 2 }, 1..5),(map { $_ / 2, $_ * 2 }, 1..5));
  say $a[0;0;0]; # 2
  say $a[0;0;1]; # 0.5
  say $a[1;0;0]; # 0.5
  say $a[1;0;0]; # 2

The flattening process will traverse into Parcels and Captures, so:

  1, (2, (3, 4))

will result in:

  1, 2, 3, 4

after flattening, while:

  1, [2, [3, 4]]

Would remain as-is.

Context deferral

Also known as "Capture Context", defines how you can defer the context coercion for a given value. That is a fundamental feature because something as simple as assigning to a scalar might imply context coercion that would get you a modified value.

Both the Parcel and the Capture are able to preserve the values as-is, in a way that you can later apply any context and have the same result as if the context was applied immediately.

Context deferral is actually the reason why Perl 6 no longer supports the "wantarray" operator, nor does it provide any substitute. The way you should implement wantarray-like behavior is by properly overriding the coercion for each context. The Contextual::Return module is an implementation of that concept in Perl 5.

In order to use the context deferral in your code, you need to use the "capture sigil", which can be presented in two forms:

  my ¢a = (1, (2, (3, 4)));


  my @%a = (1, (2, (3, 4)));

The latter is provided as an alternative for situations where you want to preserve the code in 7-bits only.

[Update: @%a might not work out as a capture sigil -- See http://irclog.perlgeek.de/perl6/2009-09-21#i_1523801.]


Please post errors and feedback to perl6-language. If you are making a general laundry list, please separate messages by topic.