Herbert Breunung > Perl6-Doc > Perl6::FAQ::Capture

Download:
Perl6-Doc-0.36.tar.gz

Annotate this POD

CPAN RT

Open  0
View/Report Bugs
Source  

NAME ^

Perl6::FAQ::Capture - Capture objects

DESCRIPTION ^

This FAQ answers questions about Capture objects, as well as their uses in bindings and function calls.

What

What happened to references?

They have been superseded by Capture objects.

What is a Capture?

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").

How

Can I see what captures look like? Does \$x no longer work?

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:

  $xcap1 === $xcap2 === $xcap3; # true

What about capturing multiple arguments?

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

How do I extract all positional arguments or all named arguments?

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.

What do you mean by "invocant"?

When you call an object's method, the self inside the method is set to that object. These are all equivalent:

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

What about multi-dispatch?

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.

What happens when a named argument is repeated?

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.

What do |$x, |@x and |%x mean?

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)

What does $x = \@y mean?

The same as $x = (@y: ). That is, a Capture of one array as invocant.

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.

(Captures are immutable; their underlying data may not be.)

So $x = @y does not mean $x = \@y anymore. Then, what does it mean?

It means assigning $x with the object bound to @y (typically an Array object).

This does not create Capture objects; to get back @y, @$x would do.

Also note that all these forms mean the same thing:

  @y;
  @@y;
  @@@y;
  @y[];
  @y[][];
  @y[][][];

Does this mean I can't have something akin to a reference to a reference?

You can, as a Capture can contain another capture object in its invocant slot:

    my $x = \\3;
    say $$$x; # same as "say 3"

Can I create a "pass-through" function that captures all arguments?

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.

What about delegating a method?

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.

How is @x = (1, 2, 3) different from @y := (1, 2, 3) ?

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.

How is @x = (1, 2, 3) different from *@y := (1, 2, 3), then?

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.

Is there any difference between $x = (1, 2, 3) and @y = (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:

    $x.push(0); # FAIL: cannot find method: List.push
    @y.push(0); # works just fine

Is there any difference between $x = [1, 2, 3] and @y = [1, 2, 3]?

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]:

    $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 

Is there any difference between $x := [1, 2, 3] and @y := [1, 2, 3]?

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 = [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

So how do Captures work with Rules?

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.

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.

Why

What prompted this change? Was there something innately lacking in refrences?

[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

syntax highlighting: