Skip to content

Latest commit

 

History

History
219 lines (152 loc) · 7.08 KB

S08-capture.pod

File metadata and controls

219 lines (152 loc) · 7.08 KB

TITLE

Synopsis 8: Capture and Argument Lists

VERSION

Created: 20 Sep 2009

Last Modified: 16 Oct 2015
Version: 3

Introduction

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 argument lists 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 List 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 Captures 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 simply carry other objects without enforcing any context on them.

Capture or List

While a Capture is the object that holds the parameters sent to a routine (positional and named), a List is a more fundamental data structure that doesn't really differentiate named arguments from positional arguments.

A list doesn't enforce any context, so a way that no flattening or coercion is done. When you examine a list object, it will include all the listed items, whether they look like named arguments or positional arguments. For example:

1, 2, :a<b>

The list represented here has 3 positional items. A List 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:

foo(1,:a<b>,2)

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 List 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 List 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) {...}
foo(|$a);

In that case, the List 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 List into a Capture, you won't be able to get the original List again, because a Capture doesn't hold the information about the position of named arguments.

Multidimensionality

Probably the most important task of Lists 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 List 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 List).

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 list 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 List, 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 List, but the infix:<,>. The parens are only required in order to define a sub-list.

On the other hand, if you bind a list 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 argument lists 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 Lists 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.

Capture context is 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.

[ The capture sigil does not exist, though left in this document for the time being pending a suitable replacement mechanism to handle context deferral.]

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)));

AUTHORS

Daniel Ruoso <[email protected]>