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 Int
s, 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. :-)
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.
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.)
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.
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.
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.
The 42 example makes sense.
But, True is a already Bool; why would it call .Bool in order to be converted to a Bool?
Regardless of the object’s type, when you write
prefix:<?>
, that gets translated into.Bool
. You’re absolutely right that the.Bool
method on theBool
type doesn’t do much; it basically looks like this: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 alreadyBool
(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.> 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.