What values should a boolean function in Perl return?

Q: What are the best ways to represent true and false consistently in libraries of Perl code?

1

/

0

?

1

/ the special empty string that Perl's native boolean operators

return ?

undef

?

()

the empty list ?

---+ QUESTION BACKGROUND

(I usually don't answer my own questions, but I will probably do so here - leaving open the possibility of a better answer by others.)

We all know that Perl is very flexible wrt booleans, as with most things.

E.g. Perl treats the following as false: undef(), number 0, even if written as 000 or 0.0, the empty string, '0' (string containing a single 0 digit). Perl treats the following as true: any other scalar values, 1, -1, a string with a space in it ' ', '00' multiple 0s in a string, "0\n" (0 followed by a newline), 'true', 'false', 'undef', etc. Plus, array and list to scalar conversions mean that you can often get away with using an empty array as a false value. (Credit http://perlmaven.com/boolean-values-in-perl for the partial list of scalar values accepted as true or false by Perl. There are similar answers all over the place, including stack overflow.)

But given all of these values that are treated as true or false, what should a Perl boolean function return?

1/0

-1/0 (nah)

1/undef

1/""

If you use any of these conventions, you will quickly find that the code that you write does not behave as nicely as normal Perl code. You will not be able to replace

Possibly return 1 or return nothing, which is almost the same as 1/(). (But I have had problems with this approach.)

Avoid mixing numeric 1/0, Perl's native conditions, undef, and other schemes in the same function or library.

---+ MEDIUM LENGTH ANSWER

Generalizing original answer:

Perl has many ways of representing truth; or, rather, Perl interprets many different values as true or false.

If you are creating a family of related functions, i.e,. a library, you are advised choose one of the following well-known schemes, and to use it consistently in your library:

Truth 1/0 - numeric - most portable to/from other languages, and more printable

Truth 1/!!0 - most like standard Perl relational operators, less portable, less printable (unless you want false to be invisible)

This answer emphasizes Boolean functions or methods, predicates. It is not trying to discuss non-Boolean functions, that return actual things like numbers or strings or refs - except briefly below.

@DaveCross suggests an additional interesting scheme

return 1 / return nothing (almost 1/(), the empty list)

a scheme that I remember from the early days of Perl - before refs, I think even before undef was a value that could be returned. But IIRC there are problems with this scheme and use warnings, possibly also ?:, so I hesitate to recommend it fully until somebody explains better how to avoid such problems. Possibly using wantarray.

---++ Choose 1/0 or 1/0!! (native Perl) and be consistent

I recommend that you choose one of these Boolean schemes, and use that scheme consistently.

The 1/0 boolean scheme is probably most portable to other languages.

The 1/!!0 scheme will make your code more closely resemble native Perl operators.

If you are using the 1/!!0 scheme, don't say "return 0", say return !!0.

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. Most Perl operators that return true or false behave
this way.

Perl operators that return true or false generally return values that
can be safely used as numbers. For example, the relational operators
in this section and the equality operators in the next one return 1
for true and a special version of the defined empty string, "" , which
counts as a zero but is exempt from warnings about improper numeric
conversions, just as "0 but true" is.

Sidenote: you can turn any value into its corresponding boolean with
double negation. This leads to the !! pseudo-operator. Very useful for
returning the generic truthy or falsey value instead of some magic
number. – amon Nov 22 '12 at 22:11

There does not seem to be any literal for these special Booleans. There are, however, many ways of producing them: (0<0), (0<1), etc. (!!1) and (!!0) are probably the nicest - especially since in some C/C++ programming circles they are used for a similar purpose. Plus, !! can be applied to an incoming truth value to "normalize" it to this "Perl standard" boolean.

---++ Anti-Recommendation: do not mix Boolean schemes
E.g., in the same function or library, do NOT do

return $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean

in one place, and then somewhere else, or in later evolutions of the same code, do

return 0;
return undef;
return '';
return ();

I.e. choose one Boolean scheme, and be consistent. Mainly, this involves being consistent about the false value; to a lesser extent the true value.

e.g. Avoid evolving code from

return $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean

to

if( $arg1 < $arg2 ) {
log_or_print('found $arg1 <$arg2');
# other stuff to do if less-than
return 1;
} else {
log_or_print('found not( $arg1 < $arg2)');
# other stuff to do if not-less-than
# which may not be the same thing as greater-than-or-equal
return 0;
}

or

if( $arg1 < $arg2 ) {
...
} else {
...
return undef;
}

Coming to Perl from somewhere else, you may think that these are equivalent, and they mostly are, but if you do things like printing boolean return values in tests you will get differences.

If you evolve code from a Perl-ish boolean operator

return $arg1 < $arg2; # returning a standard Perl 1/!!0 Boolean

evolve it to

if( $arg1 < $arg2 ) {
log_or_print('found $arg1 <$arg2');
# other stuff to do if less-than
return 1;
} else {
log_or_print('found not( $arg1 < $arg2)');
# other stuff to do if not-less-than
# which may not be the same thing as greater-than-or-equal
return !!0;
}

If you want behavior to be as close to the same. Note the !!0 on the return of false, As far as I know, there is no simpler way to construct Perl's special return value for false.

Conversely, if you want to use the 1/0 Boolean scheme, them the original code should have been written as

This is neither 1/0 nor 1/!!0, and is not consistently value/undef either.

(Note: I am not saying this pre-check is a performance optimization --- but I am saying that performance optimizations might look like the above. Performance optimization is one of my specialties, and you want such optimizations to be refactoring. It sucks when optimized code performs better, but breaks in some of the places it is used. Hence my interest in code that performs as exactly like ... whatever it is replacing, like native Perl relational operators. Exactly means exactly.)

Instead do something like the following if you are using standard Perl-ish booleans.

If you consider !! as a conversion from "meta-boolean" to "special boolean",

consider 1* or 0+ as a conversion from special boolean to ordinary 0/1 boolean.

E.g. print "test".(1*($a eq $b))."\n"

E.g. print "test".(0+($a eq $b))."\n"

?: is more general, but more verbose.

---++ Non-Boolean error returns

This question and answer emphasizes Boolean functions or methods, predicates. It is not trying to discuss non-Boolean functions, that return actual things like numbers or strings or refs - except briefly below.

It is "nice" to have the return value extended to indicate special conditions such as failure, invalid input, etc., and which may be evaluated in the context of IF statements or other control flow such as and and or operators, typically to handle such errors, e.g. to provide default values.

We will limit our discussion of non-Boolean functions to this short list:

any value / undef : for functions that return any type of value, scalar number or string, scalar ref whether blessed or unblessed.

value/undef works best when undef is not a legitimate return value and can be problematic when undef is a legitimate value.n E.g. imagine an accessor function that returns the value of a hash field, $hash->{field} -- the field might legitimately have the value { field => undef }, so returning undef dfoes not distinguish between the field not existing and the field existing but having an undef value.

Arbitrary strings, that may contextually be interpreted as numbers or booleans.

"0 but true" - I really don't want to get into this, but look at What does "0 but true" mean in Perl? for the special handling of string "0 but true". Other strings give warnings on conversion to a number, but "0 but true" does not.

"0E0" - Similarly, some Perl code returns the string "0E0" which evaluates to 0 as a number, but true as a Boolean

GLEW personal opinion: since I write code that often needs to be ported to other languages, I prefer not to take advantage of Perl-specific tricks like 0+"0 but true". "0E0" at least is more portable, if you imagine that in some other language like C a function convert_string_to_float("0E0") or convert_string_to_int("0x0"). I prefer "0x0" because it looks special with the x, and 0x0 is an integer value, whereas 0E0 is interpreted as a float in some languages, so is more likely to give an error.

---++ Possible?: return 1 or return nothing scheme (possibly 1/()?)

@DaveCross makes an interesting suggestion:

return 1 for a Boolean true value.

return nothing for a Boolean false value. That's because a bare
return will return an appropriate value depending on how the
subroutine has been called. The documentation for return says this:

If no EXPR is given, returns an empty list in list context, the undefined value in scalar context, and (of course) nothing at all in void context.

This is important as true and false values can differ subtly between scalar and list context. Imagine a subroutine like this: ...

@DaveCross goes on to show how returning any value other than the empty list results in loss of false-ness if a boolean function is evaluated in array context. Even @array=(undef) evaluates as true.

I would like this scheme to work. I think that I used it years ago, in Perl 4 or earlier, but gave up on it when use warnings started becoming the thing to do.

Insofar as I recall, I also had problems with conditional exprtessions ?: wigth this convention.