views:

3438

answers:

11

Can someone explain what exactly the string "0 but true" means in Perl? As far as I understand, it equals zero in an integer comparison, but evaluates to true when used as a boolean. Is this correct? Is this a normal behavior of the language or is this a special string treated as a special case in the interpreter?

+8  A: 

In an integer context, it evaluates to 0 (the numeric part at the beginning of the string) and is zero. In a scalar context, it's a non-empty value, so it is true.

if (int("0 but true")) { print "zero"; }

(no output)

if ("0 but true") { print "true"; }

(prints true)

Daniel Papasian
+2  A: 

When you want to write a function that returns either an integer value, or false or undef (i.e. for the error case) then you have to watch out for the value zero. Returning it is false and shouldn't indicate the error condition, so returning "0 but true" makes the function return value true while still passing back the value zero when math is done on it.

andy
+23  A: 

It's normal behaviour of the language. Quoting the perlsyn manpage:

The number 0, the strings ’0’ and ’’, the empty list "()", and "undef" are all false in a boolean context. All other values are true. Negation of a true value by "!" or "not" returns a special false value. When evaluated as a string it is treated as ’’, but as a number, it is treated as 0.

Because of this, there needs to be a way to return 0 from a system call that expects to return 0 as a (successful) return value, and leave a way to signal a failure case by actually returning a false value. "0 but true" serves that purpose.

Chris Jester-Young
Nit: '0 but true' isn't just for system calls.
Michael Carman
Some modules use the string "0E0", which evaluates to 0 as a number, but true as a boolean.
Mathieu Longtin
+22  A: 

Additionally to what others said, "0 but true" is special-cased in that it doesn't warn in numeric context:

$ perl -wle 'print "0 but true" + 3'
3
$ perl -wle 'print "0 but crazy" + 3'
Argument "0 but crazy" isn't numeric in addition (+) at -e line 1.
3
moritz
+2  A: 

See also the Stack Overflow question about the way Perl 6 handles this.

raldi
+1  A: 

"0 but true" is a string just like any other but because of perl's syntax it can serve a useful purpose, namely returning integer zero from a function without the result being "false"(in perl's eyes).

And the string need not be "0 but true". "0 but false" is still "true"in the boolean sense.

consider:

if(x)

for x:        yields:
1             -> true
0             -> false
-1            -> true
"true"        -> true
true          -> true
"false"       -> true
false         -> false # notice the quotes
"0 but true"  ->true   # 
int("0 but true") ->false

The upshot of all of this is you can have:

sub find_x()

and have this code be able to print "0" as its output:

if($x = find_x)
{
   print int($x) . "\n";
}
Frosty
+3  A: 

0 means false in Perl (and other languages related to C). For the most part, that's a reasonable behavior. Other languages (Lua for instance) treat 0 as true and provide another token (often nil or false) to represent a non-true value.

One case where the Perl way doesn't work so well is when you want to return either a number or, if the function fails for some reason, a false value. For instance, if you write a function that reads a line from a file and returns the number of characters on the line. A common usage of the function might be something like:

while($c = characters_in_line($file)){
    ...
};

Notice that if the number of characters on a particular line is 0, the while loop will end before the end of the file. So the characters_in_line function should special case 0 characters and return '0 but true' instead. That way the function will work as intended in the while loop, but also return the correct answer should it be used as a number.

Note that this isn't a built in part of the language. Rather it takes advantage of Perl's ability to interpret a string as a number. So other stings are sometimes used instead. DBI uses "0E0", for instance. When evaluated in numeric context, they return 0, but in boolean context, false.

Jon Ericson
+11  A: 

You may also see the string "0E0" used in Perl code, and it means the same thing, where 0E0 just means 0 written in exponential notation. However, since Perl only considers "0", '' or undef as false, it evaluates to true in a boolean context.

Andy Lester
+1  A: 

Another example of "0 but true":

The DBI module uses "0E0" as a return value for UPDATE or DELETE queries that didn't affect any records. It evaluates to true in a boolean context (indicating that the query was executed properly) and to 0 in a numeric context indicating that no records were changed by the query.

kixx
A: 

I just found proof that the string "0 but true" is actially built into the interpreter, like some people here already answered:

$ strings /usr/lib/perl5/5.10.0/linux/CORE/libperl.so | grep -i true
Perl_sv_true
%-p did not return a true value
0 but true
0 but true
jkramer
A: 
Kyle