What does "0 but true" mean in Perl?
Asked Answered
W

14

62

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?

Wo answered 24/9, 2008 at 21:0 Comment(2)
The string "0E0" another is like "0 but true". Instead of being hard coded it's a consequence of Perl's exponential notation. As a non-empty, non-zero string, it's true. As a number, it's 0. DBI uses it as a return value from things like execute to indicate that the SQL statement worked, but no rows were affected.Lianna
I understand the reason for the special treatment of "0 but true" for warnings when converting to a number, but I still think look_like_number should return false.Averett
D
60

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.

Drud answered 24/9, 2008 at 21:6 Comment(6)
Nit: '0 but true' isn't just for system calls.Matchbook
Some modules use the string "0E0", which evaluates to 0 as a number, but true as a boolean.Galatians
OMG Perl is so ambigous :ODemythologize
@Andrei: that's not ambiguous; it's very clear. You may find it confusing, but that's entirely different.Hybridize
@Mathieu Longtin - I like that. I like that alot.Bewhiskered
"0 but true" is special. It does not produce some warnings when you use it: perl -wle'print 0+"0 but false"' prints "Argument "0 but false" isn't numeric in addition (+) at -e line 1.", but perl -wle'print 0+"0 but true"' does not.Fourth
P
53

Because it's hardcoded in the Perl core to treat it as a number. This is a hack to make Perl's conventions and ioctl's conventions play together; from perldoc -f ioctl:

The return value of ioctl (and fcntl) is as follows:

if OS returns:      then Perl returns:

    -1              undefined value
     0              string "0 but true"
anything else       that number

Thus Perl returns true on success and false on failure, yet you can still easily determine the actual value returned by the operating system:

$retval = ioctl(...) || -1;
printf "System returned %d\n", $retval;

The special string "0 but true" is exempt from -w complaints about improper numeric conversions.

Ploughboy answered 15/3, 2011 at 21:16 Comment(4)
Also used by msgctl, semctl, sysseek, and various CPAN modules. "treat it as a number" is misleading; "0 xxx", "foo", or "123profit" are also treated as numbers in numeric context; the special thing about "0 but true" is that it intentionally doesn't cause a warning.Podite
You will find that the special defined null string return from things like the relops is also a valid number: perl -wle 'printf "%d\n", 4 > 5' produces 0 sans complaint.Heater
FWIW: The Perl DBI module uses '0E0' as a variation of '0 but true'.Biddie
That's not really a variant, it's just 0 expressed in exponential format.Ploughboy
L
46

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
Lederman answered 24/9, 2008 at 21:13 Comment(0)
T
32

The value 0 but true is a special case in Perl. Although to your mere mortal eyes, it doesn't look like a number, wise and all knowing Perl understands it really is a number.

It has to do with the fact that when a Perl subroutine returns a 0 value, it is assumed that the routine failed or returned a false value.

Imagine I have a subroutine that returns the sum of two numbers:

die "You can only add two numbers\n" if (not add(3, -2));
die "You can only add two numbers\n" if (not add("cow", "dog"));
die "You can only add two numbers\n" if (not add(3, -3));

The first statement won't die because the subroutine will return a 1. That's good. The second statement will die because the subroutine won't be able to add cow to dog.

And, the third statement?

Hmmm, I can add 3 to -3. I just get 0, but then my program will die even though the add subroutine worked!

To get around this, Perl considers 0 but true to be a number. If my add subroutine returns not merely 0, but 0 but true, my third statement will work.

But is 0 but true a numeric zero? Try these:

my $value = "0 but true";
print qq(Add 1,000,000 to it: ) . (1_000_000 + $value) . "\n";
print "Multiply it by 1,000,000: " . 1_000_000 * $value . "\n";

Yup, it's zero!

The index subroutine is a very old piece of Perl and existed before the concept of 0 but true was around. It is suppose to return the position of the substring located in the string:

index("barfoo", "foo");   #This returns 3
index("barfoo", "bar");   #This returns 0
index("barfoo", "fu");    #This returns ...uh...

The last statment returns a -1. Which means if I did this:

if ($position = index($string, $substring)) {
   print "It worked!\n";
}
else {
   print "If failed!\n";
}

As I normally do with standard functions, it wouldn't work. If I used "barfoo" and "bar" like I did in the second statement, The else clause would execute, but if I used "barfoo" and "fu" as in the third, the if clause would execute. Not what I want.

However, if the index subroutine returned 0 but true for the second statement and undef for the third statement, my if/else clause would have worked.

Tangram answered 15/3, 2011 at 21:47 Comment(0)
J
25

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.

Jonajonah answered 24/9, 2008 at 21:56 Comment(0)
N
13

It's hard-coded in Perl's source code, specifically in Perl_grok_number_flags in numeric.c.

Reading that code I discovered that the string "infinity" (case insensitive) passes the looks_like_number test too. I hadn't known that.

Nikkinikkie answered 15/3, 2011 at 21:36 Comment(1)
Some C libraries use "Infinity" (or "+Infinity" or "-Infinity") as the stringification of floating-point infinity rather than variations of "Inf".Attorneyatlaw
A
10

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)

Aram answered 24/9, 2008 at 21:4 Comment(0)
S
9

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.

Sporogenesis answered 24/9, 2008 at 21:39 Comment(2)
Was the default value for 0 being true or false pre-programmed in the languages? I don't understand why anyone would want it to be different between different languages.Scrub
Isn't 0 false and 1 true in most languages though?Bindweed
M
6

Things that are false:

  • "".
  • "0".
  • Things that stringify to those.

"0 but true" is not one of those, so it's not false.

Furthermore, Perl returns "0 but true" where a number is expected in order to signal that a function succeeded even though it returned zero. sysseek is an example of such a function. Since the value is expected to be used as a number, Perl is coded to consider it to be a number. As a result, no warnings are issued when it's used as a number, and looks_like_number("0 but true") returns true.

Other "true zeroes" can be found at http://www.perlmonks.org/?node_id=464548.

Myxomatosis answered 15/3, 2011 at 21:6 Comment(6)
But that doesn't answer why looks_like_number( "0 but true" ) is true, while looks_like_number( "0 but foobar" ) is false.Prague
@friedo, Perl doesn't return "0 but foobar" as a number.Myxomatosis
@friedo, Are you actually asking why Perl would need to return something that is zero, but evaluates to true? It's so you can easily evaluate if a function succeeded when zero is a possible return value. sysseek is an example.Myxomatosis
No, I'm asking why 0 but true looks like a number but 0 but anything else does not. They'll all evaluate to 0 in numeric context. I know what 0 but true is for.Prague
@friedo, Why would "0 but anything else", "123abc", "foo bar", etc be numbers?Myxomatosis
@Prague Because it was decided by the people writing perl. They felt they needed a string that was numerically zero, but also true and forgot they already had "0E0" (or decided explaining why that was true and a the number 0 was too difficult for beginners). As to why "0 but true" needs to look like a number, well, there is a warning for non-numbers being used in numeric contexts.Slovak
F
4

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.

Flavorful answered 25/9, 2008 at 7:16 Comment(0)
W
4

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
Wo answered 28/9, 2008 at 11:24 Comment(0)
E
3

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.

Eggers answered 24/9, 2008 at 21:5 Comment(0)
K
3

"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
"false"       -> true
"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";
}
Kilogrammeter answered 24/9, 2008 at 21:18 Comment(2)
Are you sure about that bareword false? That is not what I get when I do $ perl -wle 'print "true\n" if false;'Greiner
MarkHu, you are correct there is neither a true or false keyword in perl. I have edited the original answer.Kilogrammeter
G
2

The string ``0 but true'' is still a special case:

for arg in "'0 but true'" "1.0*('0 but true')" \
           "1.0*('0 but false')" 0 1 "''" "0.0" \
           "'false'" "'Ja'" "'Nein'" "'Oui'" \
           "'Non'" "'Yes'" "'No'" ;do
    printf "%-32s: %s\n" "$arg" "$(
        perl -we '
            my $ans=eval $ARGV[0];
            $ans=~s/^(Non?|Nein)$//;
            if ($ans) {
                printf "true:  |%s|\n",$ans
            } else {
                printf "false: |%s|", $ans
          };' "$arg"
        )"
    done

give the following: (note the ``warning''!)

'0 but true'                    : true:  |0 but true|
1.0*('0 but true')              : false: |0|
Argument "0 but false" isn't numeric in multiplication (*) at (eval 1) line 1.
1.0*('0 but false')             : false: |0|
0                               : false: |0|
1                               : true:  |1|
''                              : false: ||
0.0                             : false: |0|
'false'                         : true:  |false|
'Ja'                            : true:  |Ja|
'Nein'                          : false: ||
'Oui'                           : true:  |Oui|
'Non'                           : false: ||
'Yes'                           : true:  |Yes|
'No'                            : false: ||

... and don't forget to RTFM!

man -P'less +"/0 but [a-z]*"' perlfunc

       ... "fcntl".  Like "ioctl", it maps a 0 return from the system call
           into "0 but true" in Perl.  This string is true in boolean
           context and 0 in numeric context.  It is also exempt from the
           normal -w warnings on improper numeric conversions. ...
Ganesha answered 7/6, 2012 at 16:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.