Day 19 – False truth

Today’s advent gift teaches us how to use mixins for nefarious and confusing purposes. In fact, this feature will probably appear partly insane, but it turns out to be quite useful. Enter the but operator:

my $value = 42 but role { method Bool  { False } };
say $value;    # 42
say ?$value;   # False

So you see, we overload the .Bool method on our $value. It doesn’t affect other integers in the program, not even other 42s in the program, just this one. Normally, for Ints, the .Bool method (and therefore the prefix:<?> operator) returns whether the number is non-zero, but here we make it always return False.

In fact, there’s a shorter way to write this for enum values, of which False is one.

my $value = 42 but False;

Since False is a value of the Bool type, it will automatically overload the .Bool method, which by convention is a kind of conversion method in Perl 6. Values of other types will of course overload their corresponding conversion method.

Here’s the part that turns out to be quite useful: in Perl 5 when you put a &system call in an if statement wanting to check for success, you have to remember to negate the result of the call, since in bash only zero means success:

if ( system($cmd) == 0 ) {  # alternatively, !system($cmd) 
    # ...
}

But in Perl 6, the corresponding &run routine returns the above kind of overloaded integers; these boolify to True if and only if the return value is zero, which is the opposite of the default Int behavior, and just what we need.

if run($cmd) {  # we don't negate
    # ...
}

Oh, and here’s the part that appears insane. :-) We can overload the .Bool method of boolean values!

my $value = True but False;
say $value;    # True
say ?$value;   # False

Yes, Perl 6 allows you to shoot yourself in the foot in this particular way. Though I don’t see why anyone would want to do this except for obfuscatory purposes, I’m kinda glad Perl 6 has the presence of mind to keep track of the subtleties of that type of overloading. I know I almost don’t. :-)

8 thoughts on “Day 19 – False truth

  1. I’m confused on one issue. You say “Normally, for Ints, the .Bool method (and therefore the prefix: operator) returns whether the number is non-zero, but here we make it always return False.”

    Does it ALWAYS return False? If so, how does the overloaded &run work? How do you specify the conditions on when it returns True and when it returns False? Specifying when something returns True or False is a very useful feature. Having it ALWAYS return one specific value would be much less so, since there would be no need to every check it’s return value.

  2. I can’t imagine the example method shown is the one used for the overloaded &run, for the reason you state. However, I think it would be done similarly to this…

    return $exit_code but role { method Bool { $_ == 0 } }; # or perhaps !$_

    …though I’m sure they use a predefined type.
    (Code not guaranteed to be accurate, but I hope you get the idea.)

  3. I think run simply returns the process return values in this way:

    return ($result == 0 ?? $result but True !! $result but False);

    Thus returning a zero which tests true, or a nonzero which tests false.

  4. Maybe it should be noted that this doesn’t take away any functionality.

    $r = run($cmd);
    say $r == 42; # Still possible to do anything you’d do with a number.
    say ?($r + 0); # Will still say true according to the normal
    # non-overloaded bool role; it’s not poisonous.

  5. Dan, what Christopher and Jesper said. The “always return False” role was just an example, and you can overload the .Bool method to return things according to rules as complex as you like.

  6. The 42 example makes sense.

    But, True is a already Bool; why would it call .Bool in order to be converted to a Bool?

    1. Regardless of the object’s type, when you write prefix:<?>, that gets translated into .Bool. You’re absolutely right that the .Bool method on the Bool type doesn’t do much; it basically looks like this:

      method Bool { self }

      In other words, a kind of identity operation.

      As to why there’d be a need to call the .Bool method on something that’s already Bool (besides the trivial reason that it’s always done): there’s not really any good reason to do that… until someone overrides the method, as in this post. An optimizer that could prove that no overriding has taken place would be free to remove the call.

  7. > perl6 -e ‘my $value = True but False; say $value; say ?$value;’
    False
    False
    > perl6 -v
    This is Rakudo version 2018.10-172-ge7ce194a7 built on MoarVM version 2018.10-91-g8c67e1697
    implementing Perl 6.d.

Leave a reply to carl Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.