🔬48. How does ‘0 but True’ work in Perl 6

🔬48. How does ‘0 but True’ work in Raku

N. B. Perl 6 has been renamed to Raku. Click to read more.


In Perl 6, you can say 0 but True, and change the result of coercing zero to a Boolean value. (Notice that this is not a string but a valid language construct.)

my $v = 0 but True;
say $v;  # 0
say ?$v; # True

Let us see how that works in Rakudo.

First of all, you can soon discover that there are a few multi-variants for the infix but operator (all defined in src/core/operators.pm):

proto sub infix:<but>(|) is pure {*}
multi sub infix:<but>(Mu:D \obj, Mu:U \rolish) 
multi sub infix:<but>(Mu:D \obj, Mu:U \rolish, :$value! is raw) 
multi sub infix:<but>(Mu:U \obj, Mu:U \rolish) 
multi sub infix:<but>(Mu \obj, Mu:D $val) is raw 
multi sub infix:<but>(Mu:D \obj, **@roles) 
multi sub infix:<but>(Mu:U \obj, **@roles)

For our given example, the most suitable candidate seems to be (and it is) the one taking a defined value:

multi sub infix:<but>(Mu \obj, Mu:D $val) is raw {
    obj.clone.^mixin(GENERATE-ROLE-FROM-VALUE($val));
}

The routine immediately delegates to GENERATE-ROLE-FROM-VALUE:

sub GENERATE-ROLE-FROM-VALUE($val) {
    my $role := Metamodel::ParametricRoleHOW.new_type();
    my $meth := method () { $val };
    $meth.set_name($val.^name);
    $role.^add_method($meth.name, $meth);
    $role.^set_body_block(
        -> |c { nqp::list($role, nqp::hash('$?CLASS', c<$?CLASS>)) });
    $role.^compose;
}

Let us read it line by line as far as we can. The function is using Metamodel (src/Perl6/Metamodel/*), which is a separate big story in Perl 6 that describes the behaviour of how Perl deals with storing and manipulating objects, their attributes and methods.

So, what happens here. First, the two items are created: an instance of the ParametricRoleHOW role and a method object that returns the given value (which is True in our initial example).

Then, the method is given a name (which is Bool in our case, as the ^name method returns this class for True), and the method is added to the role referenced by the same name (Bool).

Finally, an executable code block is set and the role is composed, whatever that means; maybe we should return to metamodels in the future. Just out of curiosity, here’s the method from src/Perl6/Metamodel/ParametricRoleHOW.nqp:

method compose($obj, :$compiler_services) {
    my @rtl;
    if $!in_group {
        @rtl.push($!group);
    }
    for self.roles_to_compose($obj) {
        @rtl.push($_);
        for $_.HOW.role_typecheck_list($_) {
            @rtl.push($_);
        }
    }
    @!role_typecheck_list := @rtl;
    $!composed := 1;
    $obj
}

Back to our new object $v that was created with the simple code:

my $v = 0 but True;

This object is now equipped with the Bool method. Of course, you could call Bool directly on 0, but in that case it would be a method of the Int class. By appending the but True clause, we added the method to the object itself. This method returns True, as it was the $val argument for the call of GENERATE-ROLE-FROM-VALUE.

say 0.Bool;  # 0
say $v.Bool; # True

 

2 thoughts on “🔬48. How does ‘0 but True’ work in Perl 6”

  1. Incidentally, if you really wanted to do a hostile benchmark of Perl6, infix:but is a good candidate. It takes a full millisecond to run on my machine.
    I strongly suspect that something like $_ but True for ^1000 should involve caching so that all the objects it returns are of a same class, and just instantiate it to contain correct data when used as Int. In particular, I’m not sure whether creating a fresh role object from a value even if these values are of the same type is a) necessary to conform to the spec, and b) a good idea.

Leave a Reply

Your email address will not be published. Required fields are marked *

Retype the CAPTCHA code from the image
Change the CAPTCHA codeSpeak the CAPTCHA code