6. The dd routine of Rakudo Perl 6

In Rakudo, there is a useful routine dd, which is not a part of Perl 6 itself. It dumps its argument(s) in a way that you immediately see the type and content of a variable. For example:

$ ./perl6 -e'my Bool $b = True; dd($b)'
Bool $b = Bool::True

It works well with data of other types, for example, with arrays:

$ ./perl6 -e'my @a = < a b c >; dd(@a)'
Array @a = ["a", "b", "c"]

Today, we will look at the definition of the dd routine.

It is located in the src/core/Any.pm module as part of the Any class. The code is quite small, so let us show it here:

sub dd(|) {
    my Mu $args := nqp::p6argvmarray();
    if nqp::elems($args) {
        while $args {
            my $var  := nqp::shift($args);
            my $name := try $var.VAR.?name;
            my $type := $var.WHAT.^name;
            my $what := $var.?is-lazy
              ?? $var[^10].perl.chop ~ "... lazy list)"
              !! $var.perl;
            note $name ?? "$type $name = $what" !! $what;
        }
    }
    else { # tell where we are
        note .name
          ?? "{lc .^name} {.name}{.signature.gist}"
          !! "{lc .^name} {.signature.gist}"
          with callframe(1).code;
    }
    return
}

Call with arguments

The vertical bar, which we have already seen earlier, is a signature that captures argument lists with no type checking. It is not possible to omit it and leave empty parentheses, as in that case the routine can only be called without arguments.

Inside, some NQP-magic happens but that is quite readable for us. If there are arguments, the routine loops over them, shifting the next argument in each cycle.

Then, there is an attempt to get the name, type and content:

my $name := try $var.VAR.?name;
my $type := $var.WHAT.^name;

Notice the presence of try and ? in the method call. We already saw the pattern when we were taking about string interpolation. The ?name is only called on an object if the method exists there, and does not generate an error if not.

The content is a bit more difficult thing:

my $what := $var.?is-lazy
    ?? $var[^10].perl.chop ~ "... lazy list)"
    !! $var.perl;

The result depends on whether an object is a lazy list or not. For example, try dumping an infinite range:

$ ./perl6 -e'dd 1..∞'
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10... lazy list)

Only the first ten items are listed. For a non-lazy object, the perl method is called.

Finally, the result is printed to STDERR:

note $name ?? "$type $name = $what" !! $what;

Call with no arguments

The second branch of the dd routine is triggered when there are no arguments. In that case, the routine tries to give some information about the place where it is called. Look at the following example:

sub f() { dd }
f;

The result of running this program shows the name and the signature of the function:

sub f()

A good use case can be thus to use dd in multi-functions instead of printing manual text messages.

multi sub f(Int) { dd }
multi sub f(Str) { dd }

f(42);
f('42');

Run the program, and it prints an extremely useful debugging information:

sub f(Int)
sub f(Str)

That’s all for today. See you tomorrow!

3. Playing with the code of Rakudo Perl 6

Yesterday, we looked at the two methods of the Bool class that return strings. The string representation that the functions produce is hardcoded in the source code.

Let’s use this observation and try changing the texts.

So, here is the fragment that we will modify:

Bool.^add_multi_method('gist', my multi method gist(Bool:D:) {
    self ?? 'True' !! 'False'
});

This gist method is used to stringify a defined variable.

To make things happen, you need to have the source codes of Rakudo on your computer so that you can compile them. Clone the project from GitHub first:

$ git clone https://github.com/rakudo/rakudo.git

Compile with MoarVM:

$ cd rakudo
$ perl Configure.pl --gen-moar --gen-nqp --backends=moar
$ make

Having that done, you get the perl6 executable in the rakudo directory.

Now, open the src/core/Bool.pm file and change the strings of the gist method to use the Unicode thumbs instead of plain text:

Bool.^add_multi_method('gist', my multi method gist(Bool:D:) {
    self ?? '👍' !! '👎'
});

After saving the file, you need to recompile Rakudo. Bool.pm is in the list of files to be compiled in Makefile:

M_CORE_SOURCES = \
    src/core/core_prologue.pm\
    src/core/traits.pm\
    src/core/Positional.pm\
    . . .
    src/core/Bool.pm\
    . . .

Run make and get the updated perl6. Run it and enjoy the result:

:~/rakudo$ ./perl6
To exit type 'exit' or '^D'
> my Bool $b = True;
👍
> $b = !$b; 
👎
>

As an exercise, let us improve your local Perl 6 by adding the gist method for undefined values. By default, it does not exist, and we saw that yesterday. It means that an attempt to interpolate an undefined variable in a string will be rejected. Let’s make it better.

Interpolation uses the Str method. It is similar to both gist and perl, so you will have no difficulties in creating the new version.

This is what currently is in Perl 6:

Bool.^add_multi_method('Str', my multi method Str(Bool:D:) {
    self ?? 'True' !! 'False'
});

This is what you need to add:

Bool.^add_multi_method('Str', my multi method Str(Bool:U:) {
    '¯\_(ツ)_/¯'
});

Notice that self is not needed (and cannot be used) in the second variant.

Compile and run perl6:

$ ./perl6
To exit type 'exit' or '^D'
> my Bool $b;
(Bool)
> "Here is my variable: $b"
Here is my variable: ¯\_(ツ)_/¯
>

It works as expected. Congratulations, you’ve just changed the behaviour of Perl 6 yourself!