What is a Bool?

Perl allows pretty much any value to be evaluated in a boolean context:

if ($something) {
   ...
}

No matter what $something is, it will safely evaluate to either true or false. (With the exceptions of a few edge cases like blessed objects which are overloaded to throw an error when evaluated as booleans.)

So when a Moose class does something like this, what does it mean?

has something => (
   is  => 'ro',
   isa => 'Bool',
);

If absolutely any value could work when $self->something was accessed in boolean context, then what need is there to check what value is passed to the constructor? Should Bool basically be the same as Any, just spelled differently for documentation purposes?

So what does Moose do? The documentation says:

Bool accepts 1 for true, and undef, 0, or the empty string as false.

However, that's not the full story. Blessed objects which overload stringification are accepted, but only if the stringification returns the strings "0", "1", or the empty string at the time the type constraint is checked. If the object stringifies to something else, but also overloads boolification sensibly, then too bad. Of course when you write if ($self->something) it's the boolification overloading which matters, but Moose only checks the stringification overloading.

Moose's support for objects that overload stringification as booleans is not explicitly documented, nor is it covered at all by the Moose test suite.

What does Mouse do? Well, that's even weirder. It mostly follows Moose's documented behaviour. It accepts "1" for true, and "0", undef, and the empty string for false. But also, it accepts objects overloading boolification for false. Yes, that's right — if you overload boolification to return true, it will fail the type check. Overload it to return false, and you're golden!

So where does this leave my module Types::Standard? Well, the pure Perl implementation follows what Moose does, and the (optional) XS implementation is forked from Mouse.

For the latest release of the XS version, I've dropped support for objects which overload boolification to return false, bringing it in line with Moose's documented behaviour. I plan for the pure Perl implementation to also follow suit, dropping support for objects which overload stringification to return a boolean value.

If you need support for objects overloading boolification, a quick workaround is this:

has something => (
   is  => 'ro',
   isa => 'Any', # Bool
);

Or use coercions (example uses Types::Standard):

has something => (
   is     => 'ro',
   isa    => Bool->plus_coercions(Any, q{ !!$_ }),
   coerce => 1,
);

In the case of read-only attributes, I happen to believe accepting a blessed object as a boolean value could be harmful. The contents of the object could later change, changing the value from true to false, or vice versa, despite its read-onlyness.

1 Comment

Could you submit your change to Mouse's GitHub too ? I don't think it's weird behavior is intentional.

Leave a comment

About Toby Inkster

user-pic I'm tobyink on CPAN, IRC and PerlMonks.