Day 11 — All the Stars of Perl 6, or * ** *

Today’s blog post in this year’s Perl 6 Advent Calendar is full of snowflakes. We’ll go through the constructs that employ the * character. In Perl 6, you may call it a star (or asterisk if you prefer) or a whatever, depending on the context.

Perl 6 is not a cryptic programming language; its syntax is in many aspects much more consistent than that of Perl 5. On the other side, some areas require spending time to start feeling confident in the syntax.

Let’s go through different usages of *, starting with the most simple, aiming to understand the most brain-breaking ones such as * ** *.

The first couple of usages is simple and does not require many comments:

1. Multiplication

A single star is used for multiplication. Strictly speaking, this is an infix operator infix:, whose return value is Numeric.

say 20 * 18; # 360

2. Power

The double star ** is the exponentiation operator. Again, this is an infix: that returns the Numeric result, calculating the power for the given two values.

say pi ** e; # 22.4591577183611

* * *

The same two tokens, * or **, are also used in regexes, where they mean different things. One of the features of Perl 6 is that it can easily switch between different languages inside itself. Both regexes and grammars are examples of such inner languages, where the same symbols can mean different things from what they mean in Perl 6 itself (if they have any meaning at all there).

3. Zero or more repetitions

The * quantifier. This syntax item works similarly to its behaviour in Perl 5: allows zero or more repetitions of the atom.

my $weather = '*****';
my $snow = $weather ~~ / ('*'*) /;
say 'Snow level is ' ~ $snow.chars; # Snow level is 5

Of course, we also see here another usage of the same character, the '*' literal.

4. Min to max repetitions

The double ** is a part of another quantifier that specifies the minimum and the maximum number of repetitions:

my $operator = '..';
say "'$operator' is a valid Perl 6 operator"
    if $operator ~~ /^ '.' ** 1..3 $/;

In this example, it is expected that the dot is repeated one, two, or three times; not less and not more.

Let’s look a bit ahead, and use a star in the role (role as in theatre, not in Perl 6’s object-oriented programming) of the Whatever symbol:

my $phrase = 'I love you......';
say 'You are so uncertain...'
    if $phrase ~~ / '.' ** 4..* /;

The second end of the range is open, and the regex accepts all the phrases with more than four dots in it.

5. Slurpy arguments

A star before an array argument in a sub’s signature means a slurpy argument—the one that consumes separate scalar parameters into a single array.

list-gifts('chocolade', 'ipad', 'camelia', 'perl6');

sub list-gifts(*@items) {
    say 'Look at my gifts this year:';
    .say for @items;
}

Hashes also allow celebrating slurpy parameters:

dump(alpha => 'a', beta => 'b'); # Prints:
                                 # alpha = a
                                 # beta = b

sub dump(*%data) {
    for %data.kv {say "$^a = $^b"}
}

Notice that unlike Perl 5, the code does not compile if you omit the star in the function signature, as Perl 6 expects exactly what is announced:

Too few positionals passed; expected 1 argument but got 0

6. Slurpy-slurpy

The **@ is also working but notice the difference when you pass arrays or lists.

With a single star:

my @a = < chocolade ipad >;
my @b = < camelia perl6 >;

all-together(@a, @b);
all-together(['chocolade', 'ipad'], ['camelia', 'perl6']);
all-together(< chocolade ipad >, < camelia perl6 >);

sub all-together(*@items) {
    .say for @items;
}

Currently, each gift is printed on a separate line regardless the way the argument list was passed.

With a double star:

keep-groupped(@a, @b);
keep-groupped(['chocolade', 'ipad'], ['camelia', 'perl6']);
keep-groupped(< chocolade ipad >, < camelia perl6 >);

sub keep-groupped(**@items) {
    .say for @items;
}

This time, the @items array gets two elements only, reflecting the structural types of the arguments:

[chocolade ipad]
[camelia perl6]

or

(chocolade ipad)
(camelia perl6)

7. Dynamic scope

The * twigil, which introduces dynamic scope. It is easy to confuse the dynamic variables with global variables but examine the following code.

sub happy-new-year() {
    "Happy new $*year year!"
}

my $*year = 2018;
say happy-new-year();

If you omit the star, the code cannot be run:

Variable '$year' is not declared

The only way to make it correct is to move the definition of $year above the function definition. With the dynamic variable $*year, the place where the function is called defines the result. The $*year variable is not visible in the outer scope of the sub, but it is quite visible in the dynamic scope.

For the dynamic variable, it is not important whether you assign a new value to an existing variable or create a new one:

sub happy-new-year() {
    "Happy new $*year year!"
}

my $*year = 2018;
say happy-new-year();

{
    $*year = 2019;        # New value
    say happy-new-year(); # 2019
}

{
    my $*year = 2020;     # New variable
    say happy-new-year(); # 2020
}

8. Compiler variables

A number of dynamic pseudo-constants come with Perl 6, for example:

say $*PERL;      # Perl 6 (6.c)
say @*ARGS;      # Prints command-line arguments
say %*ENV<HOME>; # Prints home directory

9. All methods

The .* postfix pseudo-operator calls all the methods with the given name, which can be found for the given object, and returns a list of results. In the trivial case you get a scholastically absurd code:

6.*perl.*say; # (6 Int.new)

The code with stars is a bit different from doing it simple and clear:

pi.perl.say; # 3.14159265358979e0 (notice the scientific
             # format, unlike pi.say)

The real power of the .* postfix comes with inheritance. It helps to reveal the truth sometimes:

class Present {
    method giver() {
        'parents'
    }
}

class ChristmasPresent is Present {
    method giver() {
        'Santa Claus'
    }
}

my ChristmasPresent $present;

$present.giver.say;             # Santa Claus
$present.*giver.join(', ').say; # Santa Claus, parents

Just a star but what a difference!

* * *

Now, to the most mysterious part of the star corner of Perl 6. The next two concepts, the Whatever and the WhateverCode classes, are easy to mix up with each other. Let’s try to do it right.

10. Whatever

A single * can represent Whatever. Whatever in Perl 6 is a predefined class, which introduces some prescribed behaviour in a few useful cases.

For example, in ranges and sequences, the final * means infinity. We’ve seen an example today already. Here is another one:

.say for 1 .. *;

This one-liner has a really high energy conversion efficiency as it generates an infinite list of increasing integers. Press Ctrl+C when you are ready to move on.

The range 1 .. * is the same as 1 .. Inf. You can clearly see that if you go to the Rakudo Perl 6 sources and find the following definitions in the implementation of the Range class in the src/core/Range.pm file:

multi method new(Whatever \min,Whatever \max,:$excludes-min,:$excludes-max){
    nqp::create(self)!SET-SELF(-Inf,Inf,$excludes-min,$excludes-max,1);
}
multi method new(Whatever \min, \max, :$excludes-min, :$excludes-max) {
    nqp::create(self)!SET-SELF(-Inf,max,$excludes-min,$excludes-max,1);
}
multi method new(\min, Whatever \max, :$excludes-min, :$excludes-max) {
    nqp::create(self)!SET-SELF(min,Inf,$excludes-min,$excludes-max,1);
}

The three multi constructors describe the three cases: * .. *, * .. $n and $n .. *, which are immediately translated to -Inf .. Inf, -Inf .. $n and $n .. Inf.

As a side Christmas tale, here’s a tiny excursus showing that * is not just an Inf. There were two commits to src/core/Whatever.pm:

First, on 16 September 2015, “Make Whatever.new == Inf True:”

  my class Whatever {
      multi method ACCEPTS(Whatever:D: $topic) { True }
      multi method perl(Whatever:D:) { '*' }
+     multi method Numeric(Whatever:D:) { Inf }
  }

In a few weeks, on 23 October 2015, “* no longer defaults to Inf,” This is to protect extensibility of * to other dwimmy situations:

  my class Whatever {
      multi method ACCEPTS(Whatever:D: $topic) { True }
      multi method perl(Whatever:D:) { '*' }
-     multi method Numeric(Whatever:D:) { Inf }
  }

Returning to our more practical problems, let’s create our own class that makes use of the whatever symbol *. Here is a simple example of a class with a multi-method taking either an Int value or a Whatever.

class N {
    multi method display(Int $n) {
        say $n;
    }

    multi method display(Whatever) {
        say 2000 + 100.rand.Int;
    }
}

In the first case, the method simply prints the value. The second method prints a random number between 2000 and 2100 instead. As the only argument of the second method is Whatever, no variable is needed in the signature.

Here is how you use the class:

my $n = N.new;
$n.display(2018);
$n.display(*);

The first call echoes its argument, while the second one prints something random.

The Whatever symbol can be held as a bare Whatever. Say, you create an echo function and pass the * to it:

sub echo($x) {
    say $x;
}

echo(2018); # 2018
echo(*);    # *

This time, no magic happens, the program prints a star.

And now we are at a point where a tiny thing changes a lot.

11. WhateverCode

Finally, it’s time to talk about WhateverCode.

Take an array and print the last element of it. If you do it in the Perl 5 style, you’d type something like @a[-1]. In Perl 6, that generates an error:

Unsupported use of a negative -1 subscript
to index from the end; in Perl 6 please
use a function such as *-1

The compiler suggests to use a function such as *-1. Is it a function? Yes, more precisely, a WhateverCode block:

say (*-1).WHAT; # (WhateverCode)

Now, print the second half of an array:

my @a = < one two three four five six >;
say @a[3..*]; # (four five six)

An array is indexed with the range 3..*. The Whatever star as the right end of the range means to take all the rest from the array. The type of 3..* is a Range:

say (3..*).WHAT; # (Range)

Finally, take one element less. We’ve already seen that to specify the last element a function such as *-1 must be used. The same can be done at the right end of the range:

say @a[3 .. *-2]; # (four five)

At this point, the so-called Whatever-currying happens and a Range becomes a WhateverCode:

say (3 .. *-2).WHAT; # (WhateverCode)

WhateverCode is a built-in Perl 6 class name; it can easily be used for method dispatching. Let’s update the code from the previous section and add a method variant that expects a WhateverCode argument:

class N {
    multi method display(Int $n) {
        say $n;
    }

    multi method display(Whatever) {
        say 2000 + 100.rand.Int;
    }

    multi method display(WhateverCode $code) {
        say $code(2000 + 100.rand.Int);
    }
}

Now, the star in the argument list falls into either display(Whatever) or display(WhateverCode):

N.display(2018);     # display(Int $n)

N.display(*);        # display(Whatever)

N.display(* / 2);    # display(WhateverCode $code)
N.display(* - 1000); # display(WhateverCode $code)

Once again, look at the signature of the display method:

multi method display(WhateverCode $code)

The $code argument is used as a function reference inside the method:

say $code(2000 + 100.rand.Int);

The function takes an argument but where is it going to? Or, in other words, what and where is the function body? We called the method as N.display(* / 2) or N.display(* - 1000). The answer is that both * / 2 and * - 1000 are functions! Remember the compiler’s hint about using a function such as *-1?

The star here becomes the first function argument, and thus * / 2 is equivalent to {$^a / 2}, while * - 1000 is equivalent to {$^a - 1000}.

Does it mean that $^b can be used next to $^a? Sure! Make the WhateverCode block accept two arguments. How do you indicate the second of them? Not a surprise, with another star! Let us add the fourth variant of the display method to our class:

multi method display(WhateverCode $code 
                     where {$code.arity == 2}) {
    say $code(2000, 100.rand.Int);
}

Here, the where block is used to narrow the dispatching down to select only those WhateverCode blocks that have two arguments. Having this done, two snowflakes are allowed in the method call:

N.display( * + * );
N.display( * - * );

The calls define the function $code that is used to calculate the result. So, the actual operation behind the N.display( * + * ) is the following: 2000 + 100.rand.Int.

Need more snow? Add more stars:

N.display( * * * );
N.display( * ** * );

Similarly, the actual calculations inside are:

2000 * 100.rand.Int

and

2000 ** 100.rand.Int

Congratulations! You can now parse the * ** * construct as effortlessly as the compiler does it.

Homework

Perl 6 gave us so many Christmas gifts so far. Let’s make an exercise in return and answer the question: What does each star mean in the following code?

my @n = 
    ((0, 1, * + * ... *).grep: *.is-prime).map: * * * * *;
.say for @n[^5];

D’oh. I suggest we start transforming the code to get rid of all the stars and to use different syntax.

The * after the sequence operator ... means to generate the sequence infinitely, so replace it with Inf:

((0, 1, * + * ... Inf).grep: *.is-prime).map: * * * * *

The two stars * + * in the generator function can be replafced with a lambda function with two explicit arguments:

((0, 1, -> $x, $y {$x + $y} ... Inf).grep: 
    *.is-prime).map: * * * * *

Now, a simple syntax alternation. Replace the .grep: with a method call with parentheses. Its argument *.is-prime becomes a codeblock, and the star is replaced with the default variable $_. Notice that no curly braces were needed while the code was using a *:

(0, 1, -> $x, $y {$x + $y} ... Inf).grep({
    $_.is-prime
}).map: * * * * *

Finally, the same trick for .map: but this time there are three arguments for this method, thus, you can write {$^a * $^b * $^c} instead of * * * * *, and here’s the new variant of the complete program:

my @n = (0, 1, -> $x, $y {$x + $y} ... Inf).grep({
        $_.is-prime
    }).map({
        $^a * $^b * $^c
    });
.say for @n[^5];

Now it is obvious that the code prints five products of the groups of three prime Fibonacci numbers.

Additional assignments

In textbooks, the most challenging tasks are marked with a *. Here are a couple of them for your to solve yourself.

  1. What is the difference between chdir('/') and &*chdir('/') in Perl 6?
  2. Explain the following Perl 6 code and modify it to demonstrate its advantages: .say for 1...**.

❄ ❄ ❄

That’s all for today. I hope that you enjoyed the power and expressiveness of Perl 6. Today, we talked about a single ASCII character only. Imagine how vast Perl 6’s Universe is if you take into account that the language offers the best Unicode support among today’s programming languages.

Enjoy Perl 6 today and spread the word! Stay tuned to the Perl 6 Advent Calendar; more articles are waiting for your attention, the next coming already tomorrow.

Andrew Shitov

4 thoughts on “Day 11 — All the Stars of Perl 6, or * ** *

  1. Nice article! A correction is called for regarding dynamic variables, though. It does matter whether ‘my’ is used or not. If you create a new variable with ‘my’, then when that variable goes out of scope the previous value is restored. If you don’t, then changes to the value will be stored up in the caller and will persist.

    sub a() {
    say $*wow;
    }

    sub b() {
    my $*wow *= 70;
    a();
    }

    sub c() {
    $*wow /= 70;
    a();
    }

    my $*wow = 7;

    a(); # 7
    b(); # 490
    a(); # 7, again
    c(); # 0.1
    a(); # 0.1, still!

      1. I think it could confuse people to read “For the dynamic variable, it is not important whether you assign a new value to an existing variable or create a new one”. But in the spirit of fun and exploring the language, I can see that “it’s not important”. ;-)

  2. Another small correction: in 3: Zero or more repetitions, `$weather.chars` already returns 5. You might want to use something in the Match object that the regex returns instead; what you are doing is stringifying the Match to the string that is matched, that is the original string… Maybe that’s what you intended, anyway.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s