Perl6::FAQ::Capture - Capture objects
This FAQ answers questions about Capture objects, as well as their uses in bindings and function calls.
They have been superseded by Capture objects.
A Capture is an object representing arguments in a function call. More generally, Capture objects express annotated nodes with children such as capture groups returned by rules, XML DOM nodes, argument lists and more.
For example, the arguments in say("Hello", "World") is a Capture object denoted by \("Hello", "World").
say("Hello", "World")
\("Hello", "World")
Prefix \ is now the Capture constructor. These are all equivalent:
\
my $xcap1 = \$x; my $xcap2 = \($x); my $xcap3 = \($x :);
All three represent an argument list with a single invocant $x, and hence are all equivalent with each other:
$x
$xcap1 === $xcap2 === $xcap3; # true
Here's how Captures look like deferred argument lists:
sub make_car ( Str $model, Str $color? = "black", Int $doors = 4 ) { ... } # some positional, some named my $car_cap = \("Model T", doors => 2); # passing the opaque Capture as actual function arguments my $car = make_car(|$car_cap); # extracting a specific positional argument say "The thing is $car_cap[0]"; # Model T # or by name say "It has $car_cap<doors> doors" # 2 doors
As you can see above, a Capture object holds both positional and named parts. If you want to retrieve all positional parts, there are two ways:
$cap[]; # postfix .[] @$cap; # prefix @
Both forms flatten under list context, so they may be used interchangeably. However, only the postfix .[] form interpolates in a string.
.[]
Similarly, access to named arguments is available by treating the Capture as a hash:
$cap{}; # postfix .{} %$cap; # prefix %
Note that the positional parts do not include the invocant.
When you call an object's method, the self inside the method is set to that object. These are all equivalent:
self
$fh.say("Hi!"); say $fh: "Hi!"; say($fh: "Hi!");
Note that an argument list can have at most one invocant. You can construct a Capture object with an invocant using the same syntax:
my $cap = \($fh: "Hi!");
To get back the invocant from a Capture object, use the prefix "$" operator:
my $fh = $$cap;
So this Perl5ish equivalence still holds:
$x === $(\$x);
Multi-dispatch governs cases where multiple subroutines or methods share the same name, relying on the arity and types of tie-breaking parameters in their parameter list (Signature):
multi f ($x; $z) { say 'A' } multi f (Int $x, Int $y; $z) { say 'B' } multi f (Str $x, Str $y; $z) { say 'C' } f(1, 2); # goes to A f(1, 2, 3); # goes to B f('x', 'y', 'z'); # goes to C
During multi-dispatch, a tie-breaking parameter may bind to the invocant argument (e.g. for multi-methods), or one of the positional arguments.
However, regardless of single- or multi- dispatch, the argument list (Capture) can never have more than one invocant. Typically, the presence of an invocant indicates a method-call (which may fall back to a subroutine-call); the lack of invocant means a subroutine-call.
1.foo(2); # Int.foo with 1 as "self"; if not # found, fallback to foo(1, 2) foo(1: 2); # same as above bar(1, 2); # never looks at Int.bar; calls &bar # in lexical/package scope
Method/subroutine calls are determined by the presence of an invocant at the calling site. Single/multi dispatch are determined by the presence of "multi" in the declaration site. The two concepts are entirely orthogonal.
Let's look at some examples:
sub make_car (Int $doors) { say "My car has $doors doors"; } make_car( doors => 2, doors => 4 ); # 4 doors sub board_ark (@animal) { say "@animal.elems() animals boarded the ark"; } board_ark( animal => "moose", animal => "elephant" ); # 2 animals
A Capture object may hold named arguments that occur twice or more. When it's bound to a variable in the Signature, if the sigil is @, then it expands to a list of all arguments (in the order they were specified).
@
Otherwise (i.e. if it's bound to a scalar or a slurpy hash), the last argument overrides the previous ones.
The prefix | operator casts objects into Captures, and merges them t into the Capture currently being constructed (e.g. an argument list):
|
my $cap = \(1, 2, x=>42); f(|$cap); # f(1, 2, x=>42)
Array, List, Hash and Pair objects cast into Capture objects in obvious ways:
my $a = [1, 2]; f(|@$a); # f(1, 2) my $l = (1, 2); f(|@$l); # f(1, 2) my $h = {x => 42}; f(|%$h); # f(x => 42) my $p = (x => 42); f(|%$p); # f(x => 42)
It also works with @ and % variables:
%
my @a = (1, 2); f(|@a); # f(1, 2) my @a := [1, 2]; f(|@a); # f(1, 2) my %h = (x => 42); f(|%a); # f(x => 42) my %h := {x => 42}; f(|%h); # f(x => 42)
The same as $x = (@y: ). That is, a Capture of one array as invocant.
$x = (@y: )
Unlike in Perl 5, this means that you can get back the Array object with:
my @y2 := $$x; # get back the invocant
instead of:
my @y2 := @$x; # WRONG: $x contains no positional parts
Manipulating the original value via the $x capture is still possible:
$$x.push("another element");
Note the need for the extra $ sigil, implying we are accessing the captured invocant. @$x.push() would mean an attempt to add an extra positional argument into $x; this would fail as all parts are immutable in an Capture object.
@$x.push()
(Captures are immutable; their underlying data may not be.)
It means assigning $x with the object bound to @y (typically an Array object).
@y
This does not create Capture objects; to get back @y, @$x would do.
@$x
Also note that all these forms mean the same thing:
@y; @@y; @@@y; @y[]; @y[][]; @y[][][];
You can, as a Capture can contain another capture object in its invocant slot:
my $x = \\3; say $$$x; # same as "say 3"
Yes, using the \$ signature:
\$
sub f (\$args) { g(|$args) } f(1, 2, x => 42); # same as g(1, 2, x => 42)
The $args above becomes a Capture object.
$args
Perl 6 has a few other provisions for this (e.g., .wrap), but if you want more control over invocation, you can take advantage of the default Signature for methods, which puts all positionals in @_ and named arguments in %_:
@_
%_
method front_meth { $!real_obj.back_meth( |<< @_, %_ ); }
You can also take the argument list as a Capture object, and merge it with another method invocation:
method front_meth (\$args) { $!real_obj_A.back_meth( |$args ); }
This works because when there is already an invocant present, further invocants in the constructing argument list will be ignored.
The latter is an error. :-)
The := operator binds its left hand side (a Signature object) to its right hand side (a Capture object), so the latter form is akin to:
:=
sub foo (@y is rw) { ... } foo(1, 2, 3); # FAIL: three arguments passed where one is expected
However, these forms are valid:
*@y := (1,2,3); # slurpy @ @y := ((1,2,3):); # list as invocant @y := ((1,2,3),); # list as first positional @y := (y => (1,2,3)); # named binding
But they are still different from the assignment form.
This means that while @y holds the values 1, 2, and 3, you cannot modify the container itself, so this won't work:
@y.push(4); # FAIL: cannot find method: List.push
On the other hand, because variables are initialized by their sigils, so these two mean the same:
my @x := []; # new Array object my @x; # implicitly does the same thing
so @x = (1, 2, 3) would simply populate the previously allocated Array object with new elements.
@x = (1, 2, 3)
Yes. The right-hand side in both case is a single List object constructed with the list-associative infix:<,> operator, but it is flattened in the second case, and its elements are put into the previously allocated Array container bound to @y:
infix:<,>
$x.push(0); # FAIL: cannot find method: List.push @y.push(0); # works just fine
Yes. As in Perl 5, the Array constructor circumfix:[ ] does not flatten under list context, so @y receives a List with one element (an Array object), which then becomes @y[0]:
circumfix:[ ]
@y[0]
$x.elems; # 3 @y.elems; # 1
The cases below are similar to Perl 5 as well:
$x.push(0); # works - $x.elems becomes 4 @y[0].push(0); # works - @y.elems remains 1 @y.push(0); # works - @y.elems becomes 2
For the usual method-based operations, they are pretty much interchangeable:
$x.push(0); # works - $x.elems becomes 4 @y.push(0); # works - @y.elems becomes 4
However, they differ when you try to assign something into them:
$x = 42; # FAIL - Array doesn't handle scalar assignment @y = 42; # works - @y.elems is now 1
Note that $x = 42 fails because the := in $x := [1, 2, 3] changes the underlying container of $x from a Scalar into an Array. Compare this with the assignment case:
$x = 42
$x := [1, 2, 3]
$x = [1,2,3]; $x = 42; # works just fine
and also binding into an integer:
$x := 41; $x = 42; # FAIL - Int doesn't handle scalar assignment either
In the context of rules, Captures are superclasses of Match. So in the example of:
my $rv = ("x-y-z-moose" ~~ /(.)-(.)-(.)-<animal> :{ return give_birth(|$/) })
give_birth() gets called with 'x', 'y', and 'z' as positional arguments, and :animal<moose> as a named argument. give_birth() can return a Moose object - and $rv is assigned a Capture object with the Moose object in its invocant slot. $rv has the same positional and named slots as the Moose object - and you can retrieve the Moose back through $rv as Animal.
give_birth()
$rv
$rv as Animal
Nested captures in the rule then become nested Capture objects within positional slots in $/, which allows them to be retrieved as arguments for additional functions. And so you can bind annotated nodes of a tree to particular function calls, passing the data straight in thanks to the equivalence of Captures returned by rules and Captures used to invoke functions and methods. As such, Captures could be considered a natural data type for XML nodes, and provide considerable power for parsing DOMs using Rules, and providing native tree manipulations.
[needs beefing up.]
* references lose form and type
* Capture also does most of what globs did, but in a safer and saner manner
* the concept of Capture is applicable in match results
To install Perl6::Doc, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Perl6::Doc
CPAN shell
perl -MCPAN -e shell install Perl6::Doc
For more information on module installation, please visit the detailed CPAN module installation guide.