🔬 75. my $x = $x in Perl 6

What happens if you’ll try to create a new variable and immediately initialise it by itself, as shown in the following test code:

my $x = $x;

This does not work (which is expected), but Perl 6 is so kind to the user  that it gives an error message prepared especially for this case:

===SORRY!=== Error while compiling:
Cannot use variable $x in declaration to initialize itself
------> my $x = $⏏x;
  expecting any of:
  term

Let us find the place in the code where the error message is triggered. This case is captured in the Grammar of Perl 6, at the place where variable is parsed:

token variable {
    . . .
    | <sigil>
      [ $<twigil>=['.^'] <desigilname=desigilmetaname>
        | <twigil>? <desigilname> ]
      [ <?{ !$*IN_DECL && $*VARIABLE && $*VARIABLE eq 
        $<sigil> ~ $<twigil> ~ $<desigilname> }>
          {
              self.typed_panic: 'X::Syntax::Variable::Initializer', 
              name => $*VARIABLE
          }
      ]?
    . . .
}

The condition to throw an exception is a bit wordy, but you can clearly see here that the whole variable name is checked, including both sigil and potential twigil.

The exception itself is located in src/core/Exception.pm6 (notice that file extensions were changed from .pm to .pm6 recently), and it is used only for the above case:

my class X::Syntax::Variable::Initializer does X::Syntax {
    has $.name = '<anon>';
    method message() {
        "Cannot use variable $!name in declaration to initialize itself"
    }
}

And that’s all for today. Rakudo Perl 6 sources can be really transparent sometimes! 🙂

🔬73. Keys, values, etc. of hashes in Perl 6

Today, we will take a look at a few methods of the Hash class that return all hash keys or values or both:

> my %h = H => 'Hydrogen', He => 'Helium', Li => 'Lithium';
{H => Hydrogen, He => Helium, Li => Lithium}

> %h.keys;
(H Li He)

> %h.values;
(Hydrogen Lithium Helium)

> %h.kv;
(H Hydrogen Li Lithium He Helium)

While you may want to go directly to the src/core/Hash.pm6 file to see the definitions of the methods, you will not find them there. The Hash class is a child of Map, and all these methods are defined in src/core/Map.pm6. Getting keys and values is simple:

multi method keys(Map:D:) {
    Seq.new(Rakudo::Iterator.Mappy-keys(self))
}

multi method values(Map:D:) {
    Seq.new(Rakudo::Iterator.Mappy-values(self))
}

For the kv method, more work has to be done:

multi method kv(Map:D:) {
    Seq.new(class :: does Rakudo::Iterator::Mappy {
        has int $!on-value;

        method pull-one() is raw {
            . . .
        }
        method skip-one() {
            . . .
        }
        method push-all($target --> IterationEnd) {
            . . .
        }
    }.new(self))
}

As you see, the method returns a sequence that is built using an anonymous class implementing the Rakudo::Iterator::Mappy role. We already saw how this approach is used in combination with defining pull-one and push-all methods.

Let us look at another set of methods, pairs and antipairs. One of them is simple and straightforward:

multi method pairs(Map:D:) {
    Seq.new(self.iterator)
}

Another one is using an intermediate class:

multi method antipairs(Map:D:) {
    Seq.new(class :: does Rakudo::Iterator::Mappy {
        method pull-one() {
            . . .
        }
        method push-all($target --> IterationEnd) {
        . . .
        }
    }.new(self))
}

Both methods produce results of the same structure:

> %h.antipairs
(Hydrogen => H Lithium => Li Helium => He)

> %h.pairs
(H => Hydrogen Li => Lithium He => Helium)

 

🔬72. Superscripts in Perl 6

In Perl 6, you can use superscript indices to calculate powers of numbers, for example:

> 2⁵
32

> 7³
343

It also works with more than one digit in the superscript:

> 10¹²
1000000000000

You can guess that the above cases are equivalent to the following:

> 2**5
32
> 7**3
343

> 10**12
1000000000000

But the question is: How on Earth does it work? Let us find it out.

For the Numeric role, the following operation is defined:

proto sub postfix:<ⁿ>(Mu $, Mu $) is pure {*}
multi sub postfix:<ⁿ>(\a, \b) { a ** b }

Aha, that is what we need, and the superscript notation is converted to the simple ** operator here.

You can visualise what exactly is passed to the operation by printing the operands:

multi sub postfix:<ⁿ>(\a, \b) { 
    nqp::say('# a = ' ~ a);
    nqp::say('# b = ' ~ b);
    a ** b 
}

In this case, you’ll see the following output for the test examples above:

> 2⁵
# a = 2
# b = 5

> 10¹²
# a = 10
# b = 12

Now, it is time to understand how the postfix that extracts superscripts works. Its name, , written in superscript, should not mislead you. This is not a magic trick of the parser, this is just a name of the symbol, and it can be found in the Grammar:

token postfix:sym<ⁿ> {
    <sign=[⁻⁺¯]>? <dig=[⁰¹²³⁴⁵⁶⁷⁸⁹]>+ <O(|%autoincrement)>
}

You see, this symbol is a sequence of superscripted digits with an optional sign before them. (Did you think of a sign before we reached this moment in the Grammar?)

Let us try negative powers, by the way:

> say 4⁻³
# a = 4
# b = -3
0.015625

Also notice that the whole construct is treated as a postfix operator. It can also be applied to variables, for example:

> my $x = 9
9
> say $x²
# a = 9
# b = 2
81

So, a digit in superscript is not a part of the variable’s name.

OK, the final part of the trilogy, the code in Actions, which parses the index:

method postfix:sym<ⁿ>($/) {
    my $Int := $*W.find_symbol(['Int']);
    my $power := nqp::box_i(0, $Int);
    for $<dig> {
        $power := nqp::add_I(
           nqp::mul_I($power, nqp::box_i(10, $Int), $Int),
           nqp::box_i(nqp::index("⁰¹²³⁴⁵⁶⁷⁸⁹", $_), $Int),
           $Int);
    }

    $power := nqp::neg_I($power, $Int) 
        if $<sign> eq '⁻' || $<sign> eq '¯';
    make QAST::Op.new(:op<call>, :name('&postfix:<ⁿ>'), 
                      $*W.add_numeric_constant($/, 'Int', $power));
}

As you can see here, it scans the digits and updates the $power variable by adding the value at the next decimal position (it is selected in the code above).

The available characters are listed in a string, and to get its value, the offset in the string is used. The $<dig> match contains a digit, you can see it in the Grammar:

<dig=[⁰¹²³⁴⁵⁶⁷⁸⁹]>+

 

🔬71. Implementing Int.sleep() in Perl 6

Hello! Yesterday, I was giving my Perl 6 Intro course at the German Perl Workshop in Gummersbash. It was a great pleasure to prepare and run this one-day course, and, while it was difficult to cover everything, we touched all main aspects of the Perl 6 language: from variables to regexes and parallel computing. Of course, it was only a top-level overview, and there was not enough time to make all the exercises. You can do them at home, here’s the Perl 6 Intro – Exercises PDF file.

Among the rest, we tried to implement the sleep method for integers. The rationale behind that is that it is possible to say:

> 10.rand
9.9456903794802

But not:

> 10.sleep
No such method 'sleep' for invocant of type 'Int'
  in block <unit> at <unknown file> line 1

OK, so let’s first implement the simplest form of sleep for Ints only. Go to src/core/Int.pm6 and add the following:

my class Int does Real {

    method sleep() {
        nqp::sleep($!value);
    }

Here’s a photo from the screen:

29695497_10156162162038326_7927919948344098147_n

There is no declaration of the $!value attribute in this file, but we know that it can be found somewhere in Perl6/Metamodel/BOOTSTRAP.nqp:

# class Int is Cool {
# has bigint $!value is box_target;
Int.HOW.add_parent(Int, Cool);
Int.HOW.add_attribute(Int,
    BOOTSTRAPATTR.new(:name<$!value>, :type(bigint), 
                      :box_target(1), :package(Int)));
Int.HOW.set_boolification_mode(Int, 6);
Int.HOW.publish_boolification_spec(Int);
Int.HOW.compose_repr(Int);

Compile and run. The desired code works now:

> 3.sleep
# sleeping 3 seconds
>

What can be changed here? The first idea is to allow non-integer numbers as the delay duration. As Int does the Real role, just move the method to src/core/Real.pm and get the value using the Num method instead of reading $!value directly (there is no such attribute in the Real role):

my role Real does Numeric {

    method sleep() { 
        nqp::sleep(self.Num);
    }

Now it also works with rationals and floating-point numbers:

> 2.sleep
2

> 3.14.sleep
3.14

> pi.sleep
3.14159265358979

Before wrapping it up, let us take a look at the body of the sleep subroutine. It is defined in src/core/Date.pm6:

proto sub sleep(|) {*}
multi sub sleep(--> Nil) { sleep(*) }
multi sub sleep($seconds --> Nil) {
    # 1e9 seconds is a large enough value that still makes VMs sleep
    # larger values cause nqp::sleep() to exit immediatelly (esp. on 32-bit)
    if nqp::istype($seconds,Whatever) || $seconds == Inf {
        nqp::sleep(1e9) while True;
    }
    elsif $seconds > 1e9 {
        nqp::sleep($_) for gather {
            1e9.take xx ($seconds / 1e9);
            take $seconds - 1e9 * ($seconds / 1e9).Int;
        }
    }
    elsif $seconds > 0e0 {
        nqp::sleep($seconds.Num);
    }
}

The code is very clear and does not need any comments.

And maybe just to see why our modified Rakudo printed the time after sleep in the tests above, let’s refer to the documentation of NQP to see that its sleep function’s return value is the number of seconds:

## sleep
* `sleep(num $seconds --> num)`

Sleep for the given number of seconds (no guarantee is made
how exact the time sleeping is spent.)
Returns the passed in number.

 

🔬70. Examining the enum type in Perl 6

In Perl 6, you can create enumerations like this:

enum colour <red orange yellow green blue violet>;

Having this said, you can use the new name as a type name and create variables of that type:

my colour $c;

$c = green;
say $c;     # green
say $c.Int; # 3

As you would rightly expect, the type of the variable is very predictable:

say $c.^name; # colour

Now, try to find the class implementation in Rakudo sources. Surprisingly, there is no file src/core/Enum.pm, but instead, there is src/core/Enumeration.pm. Looking at that file, you cannot say how our program works. Let us dig a bit.

In Grammar (src/Perl6/Grammar.nqp), you can find the following piece:

proto token type_declarator { <...> }

token type_declarator:sym<enum> {
    . . .
}

So, the enum is not a name of the data type but a predefined keyword, one of a few that exist for type declarations (together with subset and constant).

The token starts with consuming the keyword and making some preparations, which are not very interesting for us at the moment:

<sym><.kok>
:my $*IN_DECL := 'enum';
:my $*DOC := $*DECLARATOR_DOCS;
{ $*DECLARATOR_DOCS := '' }
:my $*POD_BLOCK;
:my $*DECLARAND;
{
    my $line_no := HLL::Compiler.lineof(self.orig(), self.from(), :cache(1));
    if $*PRECEDING_DECL_LINE < $line_no {
        $*PRECEDING_DECL_LINE := $line_no;
        $*PRECEDING_DECL := Mu; # actual declarand comes later, in Actions::type_declarator:sym<enum>
    }
}
<.attach_leading_docs>

Then, we expect either a name of the new type or a variable or nothing(?):

[
| <longname>
    {
     . . .
    }
| <variable>
| <?>
]

The variable part is not yet implemented:

> enum $x <a b c>
===SORRY!=== Error while compiling:
Variable case of enums not yet implemented. Sorry.
at line 2

Our test program falls to the first branch:

<longname>
  {
      my $longname := $*W.dissect_longname($<longname>);
      my @name := $longname.type_name_parts('enum name', :decl(1));
      if $*W.already_declared($*SCOPE, self.package, $*W.cur_lexpad(), @name) {
          $*W.throw($/, ['X', 'Redeclaration'],
                    symbol => $longname.name(),
          );
      }
  }

For example, if you declare enum colour, then the $longname.name() returns colour colour. Thus, we extracted it. (Also notice how redeclaration is handled.)

Finally, here is the rest of the token body:

{ $*IN_DECL := ''; }
<.ws>
<trait>*
:my %*MYSTERY;
[ <?[<(«]> <term> <.ws> || <.panic: 'An enum must supply an expression using <>, «», or ()'> ]
<.explain_mystery> <.cry_sorrows>

Indeed, we need to explain the mystery here. So, there’s room for optional traits, fine:

<trait>*

There’s another construct that should match to avoid panic messages:

<?[<(«]> <term> <.ws>

Don’t be confused by the different number of opening and closing angle brackets here. The first part is a forward assertion with a character class:

<?  [<(«]  >

It looks if there is one of the <, (, or « opening bracket at this position. The panic message is displayed if it is not found there.

Our next expected guest is a term. Obviously, the whole part <red orange . . . violet> matches with it. Not that bad; what we need to do now is to understand what happens next.

🔬68. The smartness of the sequence operator in Perl 6, part 1

In Perl 6, you can ask the sequence operator to build a desired sequence for you. It can be arithmetic or geometric progression. All you need is to show the beginning of the sequence to Perl, for example:

.say for 3, 5 ... 11;

This prints numbers 3, 5, 7, 9, and 11. Or:

.say for 2, 4, 8 ... 64;

This code prints powers of 2 from 2 to 64: 2, 4, 8, 16, 32, and 64.

I am going to try understanding how that works in Rakudo. First of all, look into the src/core/operators.pm file, which keeps a lot of different operators, including a few versions of the ... operator. The one we need looks really simple:

multi sub infix:<...>(\a, Mu \b) {
    Seq.new(SEQUENCE(a, b).iterator)
}

Now, the main work is done inside the SEQUENCE sub. Before we dive there, it is important to understand what its arguments a and b receive.

In the case of, say, 3, 5 ... 11, the first argument is a list 3, 5, and the second argument is a single value 11.

These values land in the parameters of the routine:

sub SEQUENCE(\left, Mu \right, :$exclude_end) {
    . . .
}

What happens next is not that easy to grasp. Here is a screenshot of the complete function:

sequence

It contains about 350 lines of code and includes a couple of functions. Nevertheless, let’s try.

What you see first, is creating iterators for both left and right operands:

my \righti := (nqp::iscont(right) ?? right !! [right]).iterator;
my \lefti := left.iterator;

Then, the code loops over the left operand and builds an array @tail out of its data:

while !((my \value := lefti.pull-one) =:= IterationEnd) {
    $looped = True;
    if nqp::istype(value,Code) { $code = value; last }
    if $end_code_arity != 0 {
        @end_tail.push(value);
        if +@end_tail >= $end_code_arity {
            @end_tail.shift xx (@end_tail.elems - $end_code_arity)
                unless $end_code_arity ~~ -Inf;

            if $endpoint(|@end_tail) {
                $stop = 1;
                @tail.push(value) unless $exclude_end;
                last;
            }
        }
    }
    elsif value ~~ $endpoint {
        $stop = 1;
        @tail.push(value) unless $exclude_end;
        last;
    }
    @tail.push(value);
}

I leave you reading and understand this piece of code as an exercise, but for the given example, the @tail array will just contain two values: 3 and 5.

> .say for 3,5...11;
multi sub infix:<...>(\a, Mu \b)
List    # nqp::say(a.^name);
~~3     # nqp::say('~~' ~ value);
~~5     # nqp::say('~~' ~ value);
elems=2 # nqp::say('elems='~@tail.elems);
0=3     # nqp::say('0='~@tail[0]);
1=5     # nqp::say('1='~@tail[1]);

This output shows some debug data print outs that I added to the source code to see how it works. The green comments show the corresponding print instructions.

That’s it for today. See you tomorrow with more stuff from the sequence operator. Tomorrow, we have to understand how the list 3, 5 tells Perl 6 to generate increasing values with step 1.

🔬67. Redeclaration of a symbol in Perl 6

Today, we will see how Perl 6 helps to keep our programs better.

Redeclaration of a variable

Examine the following program:

my $x = 1;
my $x = 2;
say $x;

You can immediately see that this program is not entirely correct. Either we meant to assign a new value to $x or to create a new variable with a different name. In either case, compiler has no idea and complains:

$ perl6 redecl.pl 
Potential difficulties:
    Redeclaration of symbol '$x'
    at /Users/ash/redecl.pl:2
    ------> my $x⏏ = 2;
2

You see a runtime warning, while the program does not stop. Let us find out where it happens in the source code.

When you declare a variable, the grammar matches the corresponding text and calls the variable_declarator action method. It is quite compact but nevertheless I will not quote it completely.

class Perl6::Actions is HLL::Actions does STDActions {
    . . .

    method variable_declarator($/) {
        . . .
    }

    . . .
}

By the way, you can see here how Perl 6 treats a variable name:

 my $past := $<variable>.ast;
 my $sigil := $<variable><sigil>;
 my $twigil := $<variable><twigil>;
 my $desigilname := ~$<variable><desigilname>;
 my $name := $sigil ~ $twigil ~ $desigilname;

The name of a variable is a concatenation of a sigil, a twigil and an identifier (which is called desigiled name in the code).

Then, if we’ve got a proper variable name, check it against an existing lexpad:

if $<variable><desigilname> {
    my $lex := $*W.cur_lexpad();
    if $lex.symbol($name) {
        $/.typed_worry('X::Redeclaration', symbol => $name);
    }

If the name is known, generate a warning. If everything is fine, create a variable declaration:

make declare_variable($/, $past, ~$sigil, ~$twigil, $desigilname,
                      $<trait>, $<semilist>, :@post);

Redeclaration of a routine

Now, let us try to re-create a subroutine:

sub f() {}
sub f() {}

This may only be OK if the subs are declared as multi-subs. With the given code, the program will not even compile:

===SORRY!=== Error while compiling /Users/ash/redecl.pl
Redeclaration of routine 'f' (did you mean to declare a multi-sub?)
at /Users/ash/redecl.pl:6
------> sub f() {}⏏<EOL>

This time, it happens in a much more complicated method, routine_def:

method routine_def($/) {
     . . .

     my $predeclared := $outer.symbol($name);
     if $predeclared {
         my $Routine := $*W.find_symbol(['Routine'], :setting-only);
         unless nqp::istype( $predeclared<value>, $Routine)
                && nqp::getattr_i($predeclared<value>, $Routine, '$!yada') {
              $*W.throw($/, ['X', 'Redeclaration'],
                        symbol => ~$<deflongname>.ast,
                        what => 'routine',
              );
         }
     }

The exception

The code of the exception is rather simple. Here it is:

my class X::Redeclaration does X::Comp {
    has $.symbol;
    has $.postfix = '';
    has $.what = 'symbol';
    method message() {
        "Redeclaration of $.what '$.symbol'"
        ~ (" $.postfix" if $.postfix)
        ~ (" (did you mean to declare a multi-sub?)" if $.what eq 'routine');
    }
}

As you see, depending on the value of $.what, it prints either a short message or adds a suggestion to use the multi keyword.

🔬65. The EVAL routine in Perl 6, part 2

Welcome back! As you might notice, there was a small gap in the daily post flow.

Before we are back to the Rakudo internals, a couple of words about some changes here. First of all, every post is now marked with either 🦋 or 🔬 (or with indistinguishable rectangles □ if your browser cannot display an emoji :-). These characters mean two categories of posts here: a butterfly stands for Perl 6 syntax, while a microscope is for Perl 6 internals. In the first category, only user-level aspects or Perl 6 are discussed. In the second, we dig into the source codes of Rakudo. All the past post are updated accordingly.

The second change is that I will occasionally post more articles in the Perl 6 syntax category because I found out that non-Russian speakers often like my Russian blog posts. Those posts are mostly short texts explaining interesting features of Perl 6, such as the =~= operator or promises.

* * *

OK, now we have to talk about the EVAL routine. It is defined in the src/core/ForeignCode.pm file as a multi-function. Let us see at their signatures:

proto sub EVAL($code is copy where Blob|Cool, 
               Str() :$lang = 'perl6',
               PseudoStash :$context, *%n)

multi sub EVAL($code, 
               Str :$lang where { ($lang // '') eq 'Perl5' },
               PseudoStash :$context)

Notice that one of the function is a proto, while another is the only multi-candidate. Unlike many other cases that you can see in the sources of Rakudo, this proto routine contains code. Refer to one of the recent blog posts to see how it works.

We start with an example from the first part of the article.

EVAL('say 123');

Here, the passed value is Str, and it is caught by the proto sub, as its first argument can be Cool.

The sub creates a compiler for the given language (which is Perl 6 by default).

my $compiler := nqp::getcomp($lang);

The next step in the sub is to make a string out of $code. In this first example, this task is trivial.

$code = nqp::istype($code,Blob) ?? $code.decode(
    $compiler.cli-options<encoding> // 'utf8'
) !! $code.Str;

Finally, the string is compiled:

my $compiled := $compiler.compile:
    $code,
    :outer_ctx($eval_ctx),
    :global(GLOBAL),
    :mast_frames(mast_frames),
    |(:optimize($_) with nqp::getcomp('perl6').cli-options<optimize>),
    |(%(:grammar($LANG<MAIN>), :actions($LANG<MAIN-actions>)) if $LANG);

After compilation, you get an object of the ForeignCode type. This class is a child class of Callable, so the object can be called and returned (actually, it’s not quite clear how it happens):

$compiled();

Now you can understand that single quotes in the second example with curly braces still create an executable code:

EVAL('say {456}');

Here, the whole string is compiled as it was a Perl 6 code, and the code block there is a code block, which Perl should execute, and thus say gets a code block, so it calls its gist method to prepare the output:

> {456}.gist
-> ;; $_? is raw { #`(Block|140388575216888) ... }

 

🔬61. Declared in BOOTSTRAP

First of all, a new release of the Rakudo Perl 6 compiler was announced today: 2018.02. There are many fixes and speed improvements there, including one proposed by me. Let me not go through the changes, as most of them require quite in-depth knowledge of the Rakudo internals.

Instead, let us take a low-hanging fruit and look at the feature that you may see almost immediately when you start reading Rakudo sources.

Ideologically, Perl 6 can (and should) be written in Perl 6. Currently, some parts are written in NQP but still, the vast number of data types—located in the src/core directory—are implemented in Perl 6.

The thing is that some classes are not fully defined there. Or their relation to other classes is not explicit. For example, here’s the whole definition of the Sub class:

my class Sub { # declared in BOOTSTRAP
    # class Sub is Routine

}

Not only you don’t see any methods here, but also its hierarchy is defined ‘via comments.’ Of course, Perl 6 is not that smart to read comments saying ‘make this code great and cool,’ so let’s see what’s going on here.

In the source tree, there is the following file: src/Perl6/Metamodel/BOOTSTRAP.nqp, where the above-mentioned relation is built.

The class itself (the Sub name) is declared as a so-called stub in the very beginning of the file:

my stub Sub metaclass Perl6::Metamodel::ClassHOW { ... };

Now, the name is known but the definition is not yet ready. We have seen a few examples earlier. Here is the part of the Sub class:

# class Sub is Routine {
Sub.HOW.add_parent(Sub, Routine);
Sub.HOW.compose_repr(Sub);
Sub.HOW.compose_invocation(Sub);

This code lets the user think that the class definition is the following, as the documentation says:

class Sub is Routine {
}

Other examples of Routine children are Method, Submethod, and Macro. The first two are also defined in BOOTSTRAP:

# class Method is Routine {
Method.HOW.add_parent(Method, Routine);
Method.HOW.compose_repr(Method);
Method.HOW.compose_invocation(Method);

# class Submethod is Routine {
Submethod.HOW.add_parent(Submethod, Routine);
Submethod.HOW.compose_repr(Submethod);
Submethod.HOW.compose_invocation(Submethod);

The classes themselves are defined in their corresponding files src/core/Method.pm and src/core/Submethod.pm:

my class Method { # declared in BOOTSTRAP
    # class Method is Routine

    multi method gist(Method:D:) { self.name }
}
my class Submethod { # declared in BOOTSTRAP
    # class Submethod is Routine

    multi method gist(Submethod:D:) { self.name }
}

Unlike them, the Marco type’s hierarchy is explicitly announced in src/core/Macro.pm:

my class Macro is Routine {
}

As you may see, the classes basically introduce their namespaces and do not add many methods to their Routine parent.

The Routine class in its turn is also defined in two places: in src/core/Routine.pm and in BOOTSTRAP.pm.

my class Routine { # declared in BOOTSTRAP
    # class Routine is Block
    # has @!dispatchees;
    # has Mu $!dispatcher_cache;
    # has Mu $!dispatcher;
    # has int $!rw;
    # has Mu $!inline_info;
    # has int $!yada;
    # has Mu $!package;
    # has int $!onlystar;
    # has @!dispatch_order;
    # has Mu $!dispatch_cache;

This time, there are many methods, some of which are added in src/core/Routine.pm using regular Perl 6 syntax, and some are added through BOOTSTRAP in NQP:

In Perl 6:

method candidates() {
    self.is_dispatcher ??
        nqp::hllize(@!dispatchees) !!
        (self,)
}

In NQP:

Routine.HOW.add_method(Routine, 'dispatcher', nqp::getstaticcode(sub ($self) {
    nqp::getattr(nqp::decont($self),
        Routine, '$!dispatcher')
    }));

Similarly, the attributes from comments are created in NQP:

Routine.HOW.add_attribute(Routine, Attribute.new(:name<@!dispatchees>, :type(List), :package(Routine)));
Routine.HOW.add_attribute(Routine, Attribute.new(:name<$!dispatcher_cache>, :type(Mu), :package(Routine)));

As far as I understand, such bootstrapping is needed because Rakudo requires some Perl 6 defined before it can compile itself. For example, if you declare Sub’s relation to Routine completely in src/core/Sub.pm, then you get an error when compiling Rakudo:

Representation for Sub must be composed before it can be serialized

🔬60. Examining the Real role of Perl 6, part 3

As promised yesterday, let us take a look at the two methods of the Real role: polymod and base.

polymod

I already devoted a post to the Int.polymod method, but the method also exists in the Real role. Let us see if it is different.

method polymod(Real:D: +@mods) {
    my $more = self;
    my $lazy = @mods.is-lazy;
    fail X::OutOfRange.new(
        :what('invocant to polymod'), :got($more), :range<0..Inf>
    ) if $more < 0; 
    gather { 
        for @mods -> $mod {
            last if $lazy and not $more;
            Failure.new(X::Numeric::DivideByZero.new:
                using => 'polymod', numerator => $more
            ) unless $mod;
            take my $rem = $more % $mod;
            $more -= $rem;
            $more /= $mod;
        }
        take $more if ($lazy and $more) or not $lazy;
    }
}

It looks familiar. Comparing to the method of Int, the separation of lazy and non-lazy lists is incorporated in the main loop. In the rest, it is again the mod operation (in the form of %) and a division (and some additional subtraction).

Try the method on the same 120 (but as a Numeric value):

> say 120.polymod(10,10)
(0 2 1)

> say 120e0.polymod(10,10)
(0 2 1)

The first method is a call of Int.polymod, while the second one is Real.polymod. The results are the same.

A final note on the method. Just notice that it also works with non-integer values:

> 120.34.polymod(3.3, 4.4)
(1.54 0.8 8)

Indeed, 1.54 + 0.8 * 3.3 + 8 * 3.3 * 4.4 = 120.34.

base

The base method converts a number to its representation in a different system, e. g., hexadecimal, octal, or in a system with 5 or 35 digits. Extrapolating hexadecimal system, you may guess that if there are 36 digits, then the digits are 0 to 9 and A to Z.

A few examples with the numbers with a floating point (actually, Rat numbers here):

> 120.34.base(10)
120.34
> 120.34.base(36)
3C.C8N1FU
> 120.34.base(3)
11110.100012
> 120.34.base(5)
440.132223

The fractional part is converted separately. The second argument of the method limits the number of digits in it. Compare:

> 120.34.base(5)
440.132223
> 120.34.base(5, 2)
440.14

I will skip the details of the method internals and will only show the most interesting parts.

The signature of the method in the src/core/Real.pm file is the following:

 method base(Int:D $base, $digits? is copy)

The documentation interprets that quite differently (although correct semantically):

method base(Real:D: Int:D $base where 2..36, $digits? --> Str:D)

The possible digits are listed explicitly (not in ranges):

my @conversion := <0 1 2 3 4 5 6 7 8 9
                   A B C D E F G H I J
                   K L M N O P Q R S T
                   U V W X Y Z>;

Finally, the last gathering of the separate digits into a resulting string is done like that, using a call to the Int.base method:

my Str $r = $int_part.base($base);
$r ~= '.' ~ @conversion[@frac_digits].join if @frac_digits;
# if $int_part is 0, $int_part.base doesn't see the sign of self
$int_part == 0 && self < 0 ?? '-' ~ $r !! $r;

The method also does some heuristics to determine the number of digits after the floating point:

my $prec = $digits // 1e8.log($base.Num).Int;
. . .
for ^$prec {
    last unless $digits // $frac;
    $frac = $frac * $base;
    push @frac_digits, $frac.Int;
    $frac = $frac - $frac.Int;
}

Compare now the method with the same method from the Int class:

multi method base(Int:D: Int:D $base) {
    2 <= $base <= 36
        ?? nqp::p6box_s(nqp::base_I(self,nqp::unbox_i($base)))
        !! Failure.new(X::OutOfRange.new(
            what => "base argument to base", :got($base), :range<2..36>))
}

In this case, all the hard work is delegated to the base_I function of NQP.

And that’s more or less all that I wanted to cover from the Real role internals.