Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What values should a boolean function in Perl return?

SHORT QUESTION

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?

  • () (i.e. the empty list)?

QUESTION BACKGROUND

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

For example, Perl treats the following as false: undef(), the number 0 (even if written as 000 or 0.0), an empty string, '0' (a 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" (a '0' followed by a newline), 'true', 'false', 'undef', etc. Plus, array-to-scalar and list-to-scalar conversions mean that you can often get away with using an empty array as a false value. (Credit to http://perlmaven.com/boolean-values-in-perl for the partial list of scalar values accepted as true or false by Perl.)

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 $a<$b by custom_less_than($a,$b) and have it work exactly the same.

Consider:

> perl -e 'use warnings; 
           sub my_eq { return ( $_[0]==$_[1] ? 1 : 0 ) }
           print "compare (1==0) <<".(1==0).">>"
                 ." to my_eq(1,0) <<".my_eq(1,0).">>"
                 ."\n" ;'
Output:
compare (1==0) <<>> to my_eq(1,0) <<0>>

What is the best known method for returning values from boolean functions in Perl when you are writing them yourself?

Perhaps you want your code to be Perl-like, potentially substitutable for Perl's existing boolean operators. Or perhaps you want numeric values like 1/0. Or perhaps you come from LISP, and expect Perl's undef to be used LISP's nil for false (but then you trip upon Perl's implicit treatment of many other values as false).

like image 954
Krazy Glew Avatar asked Sep 17 '16 00:09

Krazy Glew


People also ask

What value does a boolean value return?

Boolean expression can return TRUE, FALSE, or NULL based on the exact value they return and on the context in which this Boolean expression is used.

Is 0 true or false in Perl?

What is true and false in Perl? any string is true except " " and "0". any number is true except 0.

What is the value of true in boolean?

Boolean values and operationsConstant true is 1 and constant false is 0. It is considered good practice, though, to write true and false in your program for boolean values rather than 1 and 0. The following table shows comparisons and boolean operations. This is true if x is false, and false if x is true.

What does $_ mean in Perl?

There is a strange scalar variable called $_ in Perl, which is the default variable, or in other words the topic. In Perl, several functions and operators use this variable as a default, in case no parameter is explicitly used.


1 Answers

I return 1 for a Boolean true value. I can't really see that !!1 adds anything other than confusion.

But I usually 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:

sub some_boolean {
  if ($some_condition) {
    return 1;
  else {
    return undef; # same effect with any scalar value
  }
}

This works fine if it is called in scalar context.

if (some_boolean()) {
  ...
} else {
  ...
}

All works as expected. But if you call it in list context, things go a bit weird.

my @array = some_boolean();
if (@array) {
  ...
} else {
  ...
}

In this case, the else block is never called. In list context, your subroutine returns a list containing a single scalar (the value undef) so @array has a single element and the if (@array) test is always true.

Of course, your subroutine wasn't meant to be called in list context. But you can't control how other programmers will use your code.

But if the subroutine is written like this:

sub some_boolean {
  if ($some_condition) {
    return 1;
  } else {
    return;
  }
}

Everything will work as expected. If your subroutine is called in scalar context, it will return a scalar false value and if it is called in list context, it will return an empty list.

Returning an explicit false scalar value from a subroutine is, in my opinion, always suspect.

If you want to return an explicit scalar false value, then it's well worth checking the calling context and taking appropriate action if it's wrong.

croak 'Subroutine called in list context.' if wantarray;

Update: To answer this comment.

IIRC I tried using this approach, but gave up on it because it produced more warnings about undefined values - i.e. it was not a drop-in replacement for an existing Perl 1/!!0 comparisons, nor for 1/0. Consider perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return}} ', versus return !!@_

The job of a Boolean value is that you can ask it if it is true or false. That is all. In my opinion, you should not expect to be able to just print a Boolean value. The fact that you often can print Boolean values without triggering warnings is nice, but shouldn't be relied on. If I wanted to print a Boolean value, I would always use some kind of logic check (probably the ternary operator) to do it. So I'd write your example like this:

$ perl -wle 'print "a()=<<".(a() ? "True" : "False").">>\n"; sub a {if(@_) {return 1} else {return}}'

I'll just reiterate my original point. If you are returning any scalar value for false and there is any way that your subroutine can be called in list context (even accidentally) then you have introduced a potential bug into your code.

That, alone, is worth the trivial pain of having to decode Boolean values before printing them.

like image 170
Dave Cross Avatar answered Oct 19 '22 14:10

Dave Cross