Michael G Schwern wrote:

"my $foo = $p || $q" is not boolean. I'm not even sure you can call it
"pseudo-boolean" without understanding the surrounding code. How do you
know that $q can never be false?

The other examples in the ticket play out the same way:

bless {}, ref $class || $class;

$class being false would be quite the error. Devel::Cover can't know that
in most cases you want to ignore the 0 || 0 case because you assume $class
to always be some true value and don't think its worth testing that it
might be false.

I think this is a coverage vs correctness distinction. The idea that I was trying to convey is that while these expressions use a boolean operator for a shortcut, they aren't really about truth vs. falsity of the overall expression, *except* when they are being used as part of a conditional statement. From a coverage perspective, what should matter in "my $foo = $p || $q" is that $foo takes on the values of both $p and $q at some point during the test suite, not whether or not $foo takes on both true and false values -- coverage of that condition should be checked when $foo is used in another expression. In the "bless" case -- you're right that the case of $class being false may be of interest, but that's not what this common idiom actually does. The code will blithely pass a false value to bless (with potentially unexpected results depending on whether $class is 0, "", or undef). That failure is an example of where correctness can't be validated by coverage -- where the error lies between the ears of the programmer. :-) If $class were explictly tested, then Devel::Cover would pick it up properly, such as in "bless {}, ref $class || $class || die".

Want.pm seems to imply that this might be possible, but I don't know the
guts of Perl well enough. The concept I had was that *EXCEPT* in true
boolean context, the "$p || $q" idiom is (I think) pretty much logically
equivalent to a trinary operation "$p ? $p : $q" (ignoring potential
side effects) and thus the truth table in this situation only needs to
include the first operand, thus avoiding the false-alarm.


That assumption only works in void context.

# Don't care about the return value of die.
open FILE, $foo || die $!;

# DO care about what $q is
foo( $p || $q );

Following my logic above, here I'd argue that we *don't* care what $q is from a coverage perspective, only that both $p and $q are passed to foo at some point. However, if for foo() we have:

 sub foo { my $val = shift; print "Hi!" if $val }

We now care about what $val is from a coverage standpoint, so we get the same coverage testing that way and we pick up the case we want where $q is false in the original call. Unfortunately, this doesn't work with Devel::Cover when foo() is a built-in or subroutine imported from another module. In such a case, one option could be to use "foo( $p || $q || '' )" or, more precisely, "foo( $p || $q || $q )" -- which are awkward, but are actually forcing the program to evaluate $q as true or false.

I guess that begs the question for coverage testers which case is more common -- caring only that "$p || $q" takes on the value of $p and $q, or caring that both $p and $q take on both true and false values.

Regards,
David Golden

Reply via email to