📘 Private (closed) methods in Perl 6 classes

Now, after we have discussed inheritance, let us return to the private (or closed) methods. These methods may only be used within the class itself. Thus, you cannot call them from the programme that uses an instance of the class. Nor are they accessible in the derived classes. An exclamation mark is used to denote a private method.

The following example demonstrates the usage of a private method of a class. The comments in the code will help you to understand how it works.

class A {
    # Method is only available within A
    method !private {
        say "A.private";
    }

    # Public method calling a private method
    method public {
        # You cannot avoid self here.
        # Consider the '!' as a separator like '.'
        self!private;
    }
}

class B is A {
    method method {
        # Again self, but this the '.' this time.
        # This is a public method.
        self.public;

        # This will be a compile-time error.
        # self!private;
    }
}

my $b = B.new;
$b.method; # A.private

The exclamation mark is actually part of the method name. So you can have both method meth and method !meth in the same class. To access them, use self.meth and self!meth, respectively:

class C {
    method meth  {say 'meth' }
    method !meth {say '!meth'}
    method demo {
        self.meth;
        self!meth;
    }
}

my $c = C.new;
$c.demo; # Prints both meth and !meth

📘 Constructors in Perl 6 classes

You may have noticed in the previous examples that two different approaches to creating a typed variable were used.

The first was via an explicit call of the new constructor. In this case, a new instance was created.

my $a = A.new;

In the second, a variable was declared as a typed variable. Here, a container was created.

my A $a;

Creating a container means not only that the variable will be allowed to host an object of that class but also that you will still need to create that object itself.

my A $a = A.new;

Let us consider an example of a class which involves one public method and one public data field.

class A {
    has $.x = 42;
    method m {
        say "A.m";
    }
}

The internal public variable $.x is initialized with the constant value.

Now, let us create a scalar container for the variable of the A class.

my A $a;

The container is here, and we know its type, but there are no data yet. At this moment, the class method may be called. It will work, as it is a class method and does not require any instance with real data.

$a.m; # Prints “A.m”

Meanwhile, the $.x field is not available yet.

say $a.x; # Error: Cannot look up attributes in a A type object

We need to create an instance object by calling a constructor first.

my A $b = A.new;
say $b.x; # Prints 42

Please note that the initialization (= 42) only happens when a constructor is called. Prior to this, there is no object, and thus no value can be assigned to an attribute.

The new method is inherited from the Mu class. It accepts a list of the named arguments. So, this method can be used on any object with any reasonable arguments. For instance:

my A $c = A.new(x => 14);
say $c.x; # 14, not 42

Note that the name of the field (x) may not be quoted. An attempt of A.new(‘x’ => 14) will fail because it will be interpreted as a Pair being passed as a positional parameter.

Alternatively, you can use the :named(value) format for specifying named parameters:

my A $c = A.new :x(14); # Or A.new(:x(14)) if you wish
say $c.x; # 14

For the more sophisticated constructors, the class’s own BUILD submethod may be defined. This method expects to get a list of the named arguments.

class A {
    # Two fields in an object.
    # One of them will be calculated in the constructor.
    has $.str;
    has $!len;

    # The constructor expects its argument named ‘str’.
    submethod BUILD(:$str) {
        # This field is being copied as is:
        $!str = $str;

        # And this field is calculated:
        $!len = $str.chars;
    }

    method dump {
        # Here, we print the current values.
        # The variables are interpolated as usual
        # but to escape an apostrophe character from
        # the variable name, a pair of braces is added.
        "{$.str}'s length is $!len.".say;
    }
}

my $a = A.new(str => "Perl");
$a.dump;

This programme prints the following output:

Perl’s length is 4.

📘 The list method in Perl 6 channels

The list method accompanies the previously seen methods and returns everything that is left unread in the channel.

my $c = Channel.new; 

$c.send(5);
$c.send(6); 

$c.close;
say $c.list; # (5 6)

The method blocks the programme until the channel is open, thus it is wise to close it before calling the list method.

📘 The in and at methods in Perl 6 promises

The other two factory methods, Promise.in and Promise.at, create a promise, which will be kept after a given number of seconds or by a given time. For example:

my $p = Promise.in(3); 

for 1..5 {
    say $p.status;
    sleep 1;
}

The programme prints the following lines.

Planned 
Planned 
Planned 
Kept 
Kept 

That means that the promise was kept after three seconds.

📘 Whatever (*) and WhateverCode in Perl 6

In Perl 6, the star character * can be associated with one of the predefined classes, Whatever and WhateverCode.

We’ll start with an object of the Whatever class.

say *.WHAT; # (Whatever)

The construction like 1 .. * creates a Range object, where its upper limit is not fixed to any particular number.

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

Here is an example with a loop that prints the numbers from 5 to 10 line by line:

for (5 .. *) {
    .say;
    last if $_ == 10;
}

Now, try array indices and ask to take all the elements starting from the fourth one:

my @a = <2 4 6 8 10 12>;
say @a[3 .. *]; # (8 10 12)

The “three dots” operator in combination with a star creates a sequence.

say (1 ... *).WHAT; # (Seq)

You can use it when you need a lazy and potentially infinite list. A lazy list is a list whose elements are evaluated only when they are necessary for the execution of the programme.

In the following example, an array does not know its size, but you can read infinitely from it; the lazy list will supply new elements:

my @a = (100 ... *); 

for (0 .. 5) {
    say "Element $_ is @a[$_]";
}

This programme will print five lines corresponding to the first five elements of the @a array, which contain values from 100 to 105, including 105. If you change the range in the for loop from 0 .. 5 to 0 .. *, you will get a programme that prints infinitely.

It is possible to modify the algorithm for generating the new values of the sequence by giving a hint to the compiler:

my @a = (1, 2 ... *);    # step by 1
say @a[1..5];            # (2 3 4 5 6)

my @b = (2, 4 ... *);    # even numbers
say @b[1..5];            # (4 6 8 10 12)

my @c = (2, 4, 8 ... *); # powers of two
say @c[1..5];            # (4 8 16 32 64)

Together with a list repetition operator, xx, the Whatever object forms an infinite list containing the same value.

my @default_values = 'NULL' xx *;

Now, let’s move on to the WhateverCode object. It is an anonymous code block, which is a good match for simple functions, such as these:

my $f = * ** 2;  # square
say $f(16);      # 256 

my $xy = * ** *; # power of any
say $xy(3, 4);   # 81

In Perl 6, the transformation from a code with a star to an anonymous code block is called whatever-currying. In the traditional style of programming, you introduce a variable to get the same result. In Perl 6, a compiler creates that for you. The following two examples are equivalent to the two above.

# An anonymous code block with one argument $x
my $f = -> $x {$x ** 2};
say $f(16); # 256

# A block with two arguments; names are alphabetically sorted
my $xy = {$^a ** $^b};
say $xy(3, 4); # 81

Whatever-currying is also happening, for example, when we want to refer to the last elements of an array using negative indices. In the following example, we pick array elements from the fourth to the second-to-last one.

my @a = <2 4 6 8 10 12>;
say @a[3 .. *-2]; # (8 10)

In Perl 5, you could get the last element of an array with the -1 index. In Perl 6, the access @a[-1] will generate an error:

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

So, you need to add a star:

say @a[*-1]; # 12

Here, the compiler will convert @a[*-1] into the following code:

@a[@a.elems - 1]

Another common use case of WhateverCode is to provide a compiler with a rule for generating infinite sequences.

my @f = 0, 1, * + * ... *;
say @f[1..7].join(', '); # 1, 1, 2, 3, 5, 8, 13

This example creates a lazy list containing the Fibonacci numbers. The * + * construction will be implicitly replaced with something like {$^a + $^b}. Note that the first two stars in the example are part of what will become an anonymous code block, while the last one is a single Whatever object.

📘 Programming for the Internet in Perl 6

The simplest way to build a web server in Perl 6 is to use a PSGI server called Bailador. This is a module that you can find on the official page with the list of Perl 6 modules: modules.perl6.org. If you are using the Rakudo Star distribution, use the panda* command line utility to install the module.

$ panda install Bailador

Bailador copies the interface of the well-known framework Dancer for Perl 5. The name is the same but in Spanish.

Here is the minimal programme that implements the web server.

use Bailador; 

get '/' => sub {
    'Hello, world!'
} 

baile;

The programme describes the action, which the server does in response to the request to its home page. The baile (dance in Spanish) method starts the main loop of the PSGI server.

Run the programme:

$ perl6 web1.pl

You will get the output informing you that the server is ready to accept requests.

Entering the development dance floor: http://0.0.0.0:3000
[2016-12-27T20:27:34Z] Started HTTP server.

Open that page in a browser, and you will see the desired output: “Hello, world!”

The next step is to parse the URL and respond accordingly. Bailador allows extract parameters from the URL with the colon syntax:

get '/:name' => sub ($name) {
    "Hello, $name!"
}

Please note that you cannot omit the space after the sub keyword. There is an alternative. As the sub is anonymous, you may use the pointy block instead:

get '/:name' => -> $name {
    "Hello, $name!"
}

Add it to the programme, restart the server, and go to, for example, http://0.0.0.0:3000/abc. You should get the “Hello, abc” output in the browser.

Bailador is happy to accept regexes instead of the fixed URLs. For example, let’s create the URL /square-of/N, where the N can be any non-negative integer.

get / 'square-of/' (<digit>+) / => sub ($n) {
    $n * $n
}

The regex pattern / ‘square-of/’ (<digit>+) / contains the capturing part, and so the variable $n will be set to the number from the URL. As Bailador reads the address patterns in the order they appear in the file, make sure to put the method above the handler of /name. Now, test how it works at http://0.0.0.0:3000/square-of/5; it should print 25.

It is possible to access some environment variables in the URL handler. Use the request method and take the request.env hash from it, as is demonstrated in the example:

get '/ua' => sub {
    request.env<HTTP_USER_AGENT> ~
    '<br />' ~
    request.env<QUERY_STRING>
}

The page http://0.0.0.0:3000/ua?key=value will now print the user agent name and list the query parameters of the request.

After we generate the output from the Perl code, let us move to using templates. Bailador will search for the template files in the views directory.

Save a simple text template to views/test.tt, and use it in the server like this:

use Bailador; 

get '/form' => sub {
    template 'test.tt';
} 

baile;

To print something inside the template, pass the data in hash:

get '/form/:name' => sub ($name) {
    template 'name.tt', {name => $name}
}

You can access the data from a template. It receives the argument containing everything that you just passed.

% my ($params) = @_; 
Hi, <%= $params<name> %>!

At the moment of writing this book, panda was about to become outdated, and the new recommended tool will be zef. Please refer to the documentation of your Perl 6 distribution on how to install modules.