During a recent job interview process, I submitted some sample Perl code which used the so-called "secret" !!
operator. Later, when discussing the code, one of the interviewers asked me why I chose to use that, and indicated that it was considered bad form. He didn't elaborate as to why.
My team and I have been using this operator for years, without ever realizing it was considered "bad form."
Does the "bang bang" operator have side-effects or other unexpected behavior? Why is it, or might it be, considered "bad form" by some? Is there an idiomatic alternative?
Below are a few examples where I would have considered !!
acceptable and/or desirable.
The actual code in the coding exercise, which is an example of adding booleans:
while (my $line = <$F>) { # snip exists $counts{lines} and $counts{lines} += !! chomp $line; }
Using a boolean value as a hash key (clearly a simplified example):
sub foo { my ($input) = @_; my %responses = ( '' => "False", 1 => "True" ); return $responses{ !! $input }; }
Using a boolean in a bitwise operation, or even pack()
:
sub foo { my ( $a, $b, $c ) = @_; my $result = !!$a + (!! $b)<<1 + (!! $c)<<2; return $result; }
You need to do a typecast for use by an external library/process, such as a database, which only considers certain values to be truthy:
my $sth = $dbh->prepare("INSERT INTO table (flag,value) VALUES (?,?)") $sth->execute("i_haz_cheeseburger", !! $cheeseburger_string)
I'd call it 'bad form' because it simply isn't necessary. You don't need to boolean convert in Perl. And so at best it's redundant, and at worst it's an obfuscation.
While there are some really nice tricks you can use in Perl, one of the biggest problems with the language (perceptually at least) is that it's rather prone to "write once" code.
After all - why do you ever need to 'double-negate' a scalar to get a boolean, when you can just test the scalar?
my $state = !! 'some_string'; if ( $state ) { print "It was true\n" };
Or:
if ( 'some_string' ) { print "It was true\n" };
And it's true - you can write some horrifically unintelligible Perl code thanks to "secret" operators, punctuation-based variables, etc. And it'll work fine - for example - I still consider this a masterpiece: 3-D Stereogram, Self replicating source
But it isn't 'good code'. For 'real world' usage, your code needs to be clear, intelligible and easy to troubleshoot.
Regarding alternatives;
while (my $line = <$F>) { # snip exists $counts{lines} and $counts{lines} += !! chomp $line; }
This is ... counting how often you've successfully chomp
ed a line I think? So basically it just counts numbers of lines in your input.
For that, I'd be thinking 'use $.
'. Unless I've missed something profound about your code?
"What if it's not chomp?"
Well, OK. How about:
$counts{lines}++ if foo();
For this:
sub foo { my ($input) = @_; my %responses = ( "" => "False", 1 => "True" ); return $responses{ !! $input }; }
I'd be writing that as:
sub foo { my ($input) = @_; return $input ? "True" : "False"; }
For the last condition - packing flags into a byte:
sub flags { my @boolean_flags = @_; my $flags = 0; for ( @boolean_flags ) { $flags<<=1; $flags++ if $_; } return $flags; } print flags ( 1, 1, 1 );
Or perhaps if you are doing something like that - taking a leaf from how Fcntl
does it by defining values to add based on position, so you're not having to worry about the positional arguments problem:
You can request that the flock() constants (LOCK_SH, LOCK_EX, LOCK_NB and LOCK_UN) be provided by using the tag
:flock
.
And then you can:
flock ( $fh, LOCK_EX | LOCK_NB );
They're defined bitwise, so they 'add' via the or
operator - but that means it's an idempotent operation, where setting LOCK_NB
twice wouldn't be.
For example,
LOCK_UN = 8 LOCK_NB = 4 LOCK_EX = 2 LOCK_SH = 1
If we expand the question to the less subjective:
I'm asking for the reasons to avoid !!
if ( $somecondition ) { $result++ }
is clearer than $result += !! $somecondition
.!!1
is 1
, !!0
is dualvar('', 0)
. While that's quite flexible, it can surprise, especially since most people don't even know what a dualvar is, much less that !
returns one. If you use a ternary, it's explicit what values you get:$value ? 1 : 0
Your !!
takes advantage of two obscure things in Perl: The specific values that !
returns, and that one of the possible values is dualvar (a scalar containing both a string and a number). Using !!
to compound these behaviors is admittedly pithy. While its pithiness can be a huge plus, it can also be a rather big minus. Don't use features that result in a project mandating that "the use of Perl is forbidden on this project."
There are lots of alternatives to $count += !! (some_expression)
to count occurrences of truthy values of that expression. One is the ternary, $count += (some_expression) ? 1 : 0
. That too is a bit obscure. There is a nice compact way to do what you want, which is to use the post-if:
$x++ if some_expression;
This says exactly what you are doing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With