WTF is “0 but true”

Alberto de Murga
4 min readMay 7, 2021
Perl variable declaration of a string with the value “0 but true”.

I have spent most of my life coding with JavaScript. For me, it is natural to use, and I know by heart most of its quirks and oddities. I understand many of these are quite strange for newcomers and can generate some aversion. However, recently learnt about one of these things in Perl that makes JavaScript oddities look amateurish.

Perl? What is that?

Perl is a scripting programming language that was quite popular in the early ’90s and lately has fallen in usage, although at the moment of writing it is still in the top 20 in the TIOBE index. It is still widely used in bioinformatics, sysops and projects like git or the Linux kernel among many others. Perl is well known for its concise although often cryptic syntax and powerful one-liners and regular expressions. This is also its curse as it has been accused of being hard to maintain and “write-only”.

Perl shares many similarities with languages like Python, JavaScript or PHP. Its variables may have different types, each one of them prefixes the variables names with a different sigil, similar to PHP: $scalar and list, which may be @array or %hash.

Understanding Perl

The scalar represents a single value, that can be a string, a number, a float, or a reference to a list. We will not get deep into this, but the important idea is that Perl will transform between these different values depending on its needs as other scripting languages do. However, this sometimes is not explicit and it will complain if you use the wrong operator with the wrong type.

0> my $aString = "string"
$res[1] = "string"
1> $aString == "string" ? "true" : "false";
Argument "string" isn't numeric in numeric eq (==) at reply input line 1.
Argument "string" isn't numeric in numeric eq (==) at reply input line 1.
$res[2] = "true"
3> $aString eq "string" ? "true" : "false";
$res[3] = "true"

In the example above, we can see that Perl complained because we used a numeric operator instead of a string operator. Typically, we would execute the script in strict mode without warnings, which will give an error and stop execution.

Perl does not have a boolean type, but different values will be evaluated as “true” or “false”. Quoting the official documentation:

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.

What is special about "0 but true"

"0 but true" :

  • It is a string
  • that can be used as 0
  • but evaluates as true.

So, we can operate with it like any other string:

0> my $zeroButTrue = "0 but true";
$res[1] = "0 but true"
1> split " ", $zeroButTrue;
$res[2] = (0, "but", "true")

but we can also do some maths!! This will not work with other strings if we have strict mode and warnings enabled.

2> 10 + $zeroButTrue;
$res[3] = 10
3> my $oneString = "1 string";
$res[4] = "1 string"
4> 10 + $oneString;
Argument "1 string" isn't numeric in addition (+) at reply input line 1.
$res[5] = 11

However, we can see as well that this is not how 0 behaves:

5> $zeroButTrue ? "true" : "false";
$res[6] = "true"
6> 0 ? "true" : "false";
$res[7] = "false"

But why?

The reason behind this odd behaviour is the way *nix systems handle process returns. In these systems, when a process returns 0, it means success or true, the opposite as in Perl or almost any other programming language. Any other number different from 0 is an error, being the value of the output of the error code. Taking these values without processing will create output. Therefore, instead of returning 0, it will return 0 but true and if it is an error, it will return the error code as a value. Error codes are still evaluated as a number, hence they will evaluate as true.

This magic string is indeed a curiosity, and this could have been fixed by simply inverting the result of the return when evaluating (or using unless instead of if), also making the errors evaluate to false. There could be other design decisions for this choice that I am not aware of, but at the same time, it is a fun oddity that will always surprise beginners without being likely to harm them by chance.

References

--

--

Alberto de Murga

Software engineer at @bookingcom. I like to make things, and write about what I learn. I am interested in Linux, git, JavaScript and Go, in no particular order.