What values should a boolean function in Perl return?
Asked Answered
F

2

9

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).

Formwork answered 17/9, 2016 at 0:46 Comment(5)
Side-note: The string '0 but true' is a good one to know. It's recognized by Perl as a "special" 0, so even when you have strict and warnings enabled (that would object to a string with non-numeric contents being used in a numerical context), it silently gets treated as numeric 0, but as boolean true.Cambrai
@Сухой27: 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" ;' compare (1==0) <<>> to my_eq(1,0) <<0>>Formwork
By the way, I posted this Q and answered it myself because I wish that somebody had told me to use !!0 and !!1 when I started using Perl circa 1994. Instead I took my C 1/0 and my LISP t/nil (mapped to 1/undef) with me into Perl, which caused headaches over and over again.Formwork
Return 1 for true, 0 for false, and don't worry about it.Stalky
@AndyLester: Even though return 0 is true in list context?Yunyunfei
F
-2

---+ SHORT ANSWER

Here are two self-consistent schemes for Booleans in Perl:

  • 1/0 - printable and portable
  • 1/!!0 - most like Perl's native boolean functions

1/0is probably most familiar to programmers from other languages, like C or Python. You can print 1/0 Boolean values, add them, and so on. But... Perl's native Boolean operators are not 1/0 values, return $a<0 is NOT the same as return 1 if $a<0; return 0.

1/0!! is my attempt to create an abbreviated name for the scheme that Perl's native Boolean operators use: 1 for true, an empty string that is specially marked so that it does not produce warnings when used in arithmetic or interpolated into strings. !!0 is one of the easiest ways to produce this special Perl false value, and should be familiar to programmers in many languages like C as a way of standardizing Boolean values. You can add 1/0!!, and you can print 1/0 Boolean values, so long as you don't care if the false value may be invisible, i.e. an empty string.

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

When deliberately mixing, use conversion operators such as

  • !!$bool_0or1 to convert to a traditional 1/!!0Perl value
  • (0+($a<0)) to convert a Perl relational result to 1/0
  • (!!any_other_boolean())
  • (0+any_other_boolean())
  • ($other_bool?1:()) to convert to 1/() from the other boolean schemes
  • ($other_bool?1:undef) to convert to 1/undef from the other boolean schemes

Q: are there any shorter or prefix-ish notations, other than ?:?

There are some more possible schemes

  • 1/() - more precisely, return 1 or nothing -- which can catch
    some Perl-ish errors like returning a boolean scalar false like 0 or undef in a list context, where it would become true
  • (1)/(0) - return a list of length 1 conrtaining 1, or an empty list. Similar to 1/(), consistent in the sense that both true and false are arrays.
  • 1/undef - another possibility, catches errors that 1/() return 1 or nothing may cause

I hesitate to recommend these. At the very least, 1/() is inconsistent in that But... they certainly have been used by Perl programmers, so you should be prepared to deal with code that uses these schemes. I.e. be prepared to debug bugs caused by these schemes.

1/() is my attempt to create an abbreviated name for a scheme where functions that return true return 1;, and functions that return false do return;, i.e. a return with no operand value. I.e. return 1 or nothing. I believe that return nothing is equivalent to return ();. This scheme protects you against bugs caused by a programmer evaluating your function in a list context rather than a scalar context. But it exposes you to bugs such as {1=>return_nothing_if_false(),2=>return_nothing_if_false()} (snce you probably do not want {1=>2}.

BTW I think that it might be more consistent to do the scheme (1)/(). This would allow you to consistentl;y have variables of this Boolean type, admittedly @array variables.

Note that 1/undef is NOT equivalent to any of the above. 1/undef Boolean variables give warnings when false=undef is printed or interpolated or used in arithmetic, but not when true=1 is so-manipulated, and evaluates to true in a list context. One might be tempted to say that it has the worst features of all the schemes.

I hesitate to hesitate these schemes 1/(), (1)/(), or 1/undef. At the very least, 1/() is inconsistent in that

All three of these schemes 1/(), (1)/(), or 1/undef expose you to bugs such as {1=>f1_return_nothing_if_false(),2=>f2_return_nothing_if_false()} , since you probably do not want {a=>"b"} if both are false, and {a=>1,b=>1} if both are true. At least if f1 returns true and f2 returns false, or vice versa, you will get a warning about odd sized hash.

The programmer calling a function can control whether it is evaluated in list or scalar context, but she may not be able to control whether it returns true or false.

IMHO, if you do 1/(), (1)/(), or 1/undef you cannot safely call such functions in array context, like building keyword arguments foo(kw=>boolfunc(),...) or foo({kw=>boolfunc(),kw2=>...},...). Not without having to scatter !! or 0+ all over.

---+ 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:

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

  2. 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

  1. 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.

If you are using the 1/0 scheme, don't say return $a < $b, instead say return 0+($a < $b)

If you are calling code that uses a different Boolean scheme (or, possibly, no consistent scheme), convert to the Boolean scheme that you use in your code, using operators such as

  • !! to normalize a standard Perl 1/0!! Boolean
  • 0+ or 1* to convert to a more portable 1/0 boolean from a standard Perl 1/0!! boolean
  • ?: and all the rest of Perl's arsenal for undefs and strings that may or may not want to be considered false or failures

If looking at the return value of a function that returns a ref or undef

  • If 1/!!0 Perl-like Booleans, say return !!ref_retval_func() or defined ref_retval_func()

  • If 1/0 more portable Booleans, say return 0+!!ref_retval_func() or 0+(defined ref_retval_func())

Way too much detail below.

---++ 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.

---++ 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.

---+ EXCESSIVE MESSY DETAIL

---++ Discussion elsewhere about Perl's many values of truth

Posts such as What do Perl functions that return Boolean actually return and Why does Perl use the empty string to represent the boolean false value? discuss what Perl boolean functions and operators actually return. Basically special values, whose behavior is specified by the Perl manuals.

@cim links to the perl manuals: http://perldoc.perl.org/perlsyn.html#Truth-and-Falsehood

Truth and Falsehood

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.

Similarly http://perldoc.perl.org/perlop.html#Relational-Operators

Relational Operators

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.

Unfortunately, the accepted answer for What do Perl functions that return Boolean actually return discusses the internals, but then recommends

my $formatted = $result ? '1' : '0';

which is back to where we started.

@amon shows us the light (!!) in a comment on the question What do Perl functions that return Boolean actually return

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

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

---++ Creating a predicate from value / undef

Similarly, you may be tempted to take a function such as

sub find_string_in_table {
   # returns string value if found, undef if not found
   return $lookup_table->{$_[0]}; 
}

and refactor it to a predicate

sub is_string_in_table {
   return find_string_in_table(@_);
}

and then later evolve to, perhaps, have a sanity check or performance optimization.

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return find_string_in_table(@_);
}

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.

sub is_string_in_table {
   return !!0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return (defined find_string_in_table(@_));
}

or if you are using 1/0 Booleans

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return 0+(defined find_string_in_table(@_));
}

If instead of find_string_in_table it was find_object_ref_in_table, you might do just return 0+!!find_string_in_table(@_) because you would not need to worry about strings like q() and "0".

If you want boolean functions in code you write to behave like native Perl operators return (!!1) for true and (!!0) for false.

I.e. 0/1, but logically negate twice using the ! operator to convert your 1 or 0 into Perl's 'native' boolean.

e.g.

sub my_boolean_function {
      ... 
      return !!1; # true 
      ...
      return !!0; # false
}

**---+ 0/1 --> 1/!!0 conversion **

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:

  • ref / undef : for functions that return a typical http://perldoc.perl.org/perlobj.html object, a ref to a blessed hash or other type. Return undef on error, not found, etc.

  • 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.

I have tried both "return;" and "return();"

Consider

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return ()}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return @_ } '
a()=<<0>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return !!@_ } '
a()=<<>>

%

---+ BOTTOM LINE

Use 1/0 (printable and portable), 1/0!! (most like Perl's native boolean functions).

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.

Finally, if you have ever done

$>  perl -wle 'print false && true'

you may have received

Unquoted string "false" may clash with future reserved word at -e line 1.
Unquoted string "true" may clash with future reserved word at -e line 1.
Bareword found in conditional at -e line 1.
true

so it appears likely that some day Perl may have an "official" scheme for Booleans, with values true and false.

I wonder how those will behave?

Formwork answered 17/9, 2016 at 1:15 Comment(9)
Hi. I don't program in Perl, but I performed a very simple acid test on your proposed "native" !!0: namely, I evaluated print !!0, "\n";. Perl didn't print anything; just what appears to be a blank line. A false object which doesn't have a positive, non-blank printed representation consisting of some visible characters is a poor choice to recommend. I also tried printing !!1 the same way; that renders as just 1. (So is 1 the native true? Or is it some object other than 1 which looks like it when printed?) Maybe you should just stick with 1 and 0, and not be too clever.Uveitis
@Uveitis Perl's comparison operators return a value that stringifies to the empty string: print 1 == 2. So there's already a precedent for this built into the language.Tarmac
@Kaz: coincidentally I added a recommendation to use (1*$perlish_boolean) to convert from Perl's 1/special empty string to the more familair to non-Perl programmers 1/0.Formwork
@Kaz: by the way, I agree with you: every value should have a visible default print format. But what about the empty string? IMHO every language should have a print function that can make every value visible. It's just that, in Perl, the `print' function is not that nicely behaved make-every-value-visible function.Formwork
search.cpan.org/dist/perlsecret/lib/… search.cpan.org/dist/perlsecret/lib/perlsecret.pod#Bang_bangIncredulous
@xxfelixxx: thanks. I had seen Perl secret.com, and even !!, but I had not realized WHY it should be used. Basically, return !! your truth value if you want to be compatible with Perl's native Boolean ops; and 1* your truth value if you want to be more C / C++ like. And avoid functions that contain a mix of { return $a<$b;... return 1; return 0; return undef; return ''; return "0 but true"...}Formwork
I started off writing C in Perl, or, rather, the 1/0 subset. I must admit that I played around with return "1".__FILE__.':'LINEFormwork
I would strongly advise against the use of !! in production code as it reduces comprehensibility. I have seen too much wtf code from programmers who like to sprinkle !! everywhere....Incredulous
@xxfelixxx: I agree with you. In my first job I faced this issue a lot. See: https://mcmap.net/q/462628/-use-of-double-negation-duplicateDentation
Y
8

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.

Yunyunfei answered 18/9, 2016 at 9:37 Comment(14)
Good points. If returning nothing or using wantarray is the best thing to do, then ... I will try to cram that into the answer I have just extended to handle the more portable and printable 1/0 form. Or possibly just replace the answer. Or just select your answer as the best. I would worry about users saying "Hey, this function is supposed to return a value!", so I would probably always comment such empty returns. And overall, looking for something like this is why I posted this question in the first place.Formwork
do you have a preferred way to convert some function that may not obey your "return 1/return" (return-nothing for false) convention to your convention? Apart form saying "if( foreign_function() ) { return 1; } else { return; }"Formwork
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 !!@_Formwork
It also seems not to allow ?: expressions. Sometimes you can say (cond()?1:()), not I am pretty sure I have sometimes received warnings.Formwork
But, in general, it sure would bed nice if 1/() were a consistent scheme for Booleans.Formwork
I agree with @DaveCross's comment: <<The job of a Boolean value is that you can ask it if it is true or false. ... you should not expect to be able to just print a Boolean value.>> Indeed, I like strict typing. But within loosely typed Perl, when I am refactoring, editing code to improve its structure without changing externally visible behavior, replacing "return $a<0" by `if($a<0) { return 1} else {return} is not a refactoring. Tests may break. Worse, the tests may not be tests that I have written or can change, but may be tests owned by a customer, somebody using my library.Formwork
By the way, @DaveCross, return ();, i.e. returning the empty list, the same as the return; return-nothing you advocate? I think so, but want to check. To my mind return () is a little bit more explicit. Of course it is syntactically of no value. But if I wrote programs to translate between Perl and LISP or Python or C, I might map return () to nil)/return False/return 0, and just plain Perl return; to Pythion return and C return;.Formwork
Promoting 1/() in my estimation (although pointing out the bug it exposes you to in code such as {1=>func_returning_nothing_if_false(), 2=>func_returning_nothing_if_false(),}.Formwork
BTW, wouldn't (1)/(). i.e. return a list containing the single element 1 or the empty list, be equivalent to @DaveCross's return 1 or nothing scheme? But allow you to think in terms of @booleansFormwork
Yes. return () is functionally identical to return. But why type two more characters for no gain? I have no idea what "But allow you to think in terms of @booleans" means.Yunyunfei
Consistency. Both true and false arrays. It is nice to be able to do VAR=PREDICATE(), and then use VAR later. If you do $var=pred() with 1/(), $var does not exactly contain the value of the function - if false, it contains scalar undef, not the empty list (). Later return $var, is not exactly the same as doing the original return 1 or return-nothing. If scalar/array conversions make identical, why not use 1/undef? Whereas if you always use list values for booleans, (1)/(0), you can safely use @variables to move these values around.Formwork
I anti-recommend return 1 or nothing, or 1/(), or 1/undef because with your scheme {a=>false(),b=>false()} is {a=>"b"}, but {a=>true(),b=>true()} is {a=>1,b=>21. BUG!!!! A user can control whether he is using a function is list context or scalar context, but a programmer may not know whether a Boolean function returns true or false. With your scheme, Booleans return values are not first class values that can be reliable used to build lists out of. Do you really do {a=>boolsub()?1:0,b=>boolsub()?'true':'false'} everywhere? Or scalar()? Or 0+?Formwork
"Both true and false arrays." There are no arrays involved anywhere here. Subroutines in Perl cannot return arrays. Subroutines in Perl can only ever return lists. Arrays are not lists.Yunyunfei
Fair enough: instead of returning scalar 1 if true and the empty list () false, it could consistently return the lists (1) and (). Which, if you wanted to preserve them in variables, might be preserved in an array value @retval_array=boolfunc().Formwork
F
-2

---+ SHORT ANSWER

Here are two self-consistent schemes for Booleans in Perl:

  • 1/0 - printable and portable
  • 1/!!0 - most like Perl's native boolean functions

1/0is probably most familiar to programmers from other languages, like C or Python. You can print 1/0 Boolean values, add them, and so on. But... Perl's native Boolean operators are not 1/0 values, return $a<0 is NOT the same as return 1 if $a<0; return 0.

1/0!! is my attempt to create an abbreviated name for the scheme that Perl's native Boolean operators use: 1 for true, an empty string that is specially marked so that it does not produce warnings when used in arithmetic or interpolated into strings. !!0 is one of the easiest ways to produce this special Perl false value, and should be familiar to programmers in many languages like C as a way of standardizing Boolean values. You can add 1/0!!, and you can print 1/0 Boolean values, so long as you don't care if the false value may be invisible, i.e. an empty string.

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

When deliberately mixing, use conversion operators such as

  • !!$bool_0or1 to convert to a traditional 1/!!0Perl value
  • (0+($a<0)) to convert a Perl relational result to 1/0
  • (!!any_other_boolean())
  • (0+any_other_boolean())
  • ($other_bool?1:()) to convert to 1/() from the other boolean schemes
  • ($other_bool?1:undef) to convert to 1/undef from the other boolean schemes

Q: are there any shorter or prefix-ish notations, other than ?:?

There are some more possible schemes

  • 1/() - more precisely, return 1 or nothing -- which can catch
    some Perl-ish errors like returning a boolean scalar false like 0 or undef in a list context, where it would become true
  • (1)/(0) - return a list of length 1 conrtaining 1, or an empty list. Similar to 1/(), consistent in the sense that both true and false are arrays.
  • 1/undef - another possibility, catches errors that 1/() return 1 or nothing may cause

I hesitate to recommend these. At the very least, 1/() is inconsistent in that But... they certainly have been used by Perl programmers, so you should be prepared to deal with code that uses these schemes. I.e. be prepared to debug bugs caused by these schemes.

1/() is my attempt to create an abbreviated name for a scheme where functions that return true return 1;, and functions that return false do return;, i.e. a return with no operand value. I.e. return 1 or nothing. I believe that return nothing is equivalent to return ();. This scheme protects you against bugs caused by a programmer evaluating your function in a list context rather than a scalar context. But it exposes you to bugs such as {1=>return_nothing_if_false(),2=>return_nothing_if_false()} (snce you probably do not want {1=>2}.

BTW I think that it might be more consistent to do the scheme (1)/(). This would allow you to consistentl;y have variables of this Boolean type, admittedly @array variables.

Note that 1/undef is NOT equivalent to any of the above. 1/undef Boolean variables give warnings when false=undef is printed or interpolated or used in arithmetic, but not when true=1 is so-manipulated, and evaluates to true in a list context. One might be tempted to say that it has the worst features of all the schemes.

I hesitate to hesitate these schemes 1/(), (1)/(), or 1/undef. At the very least, 1/() is inconsistent in that

All three of these schemes 1/(), (1)/(), or 1/undef expose you to bugs such as {1=>f1_return_nothing_if_false(),2=>f2_return_nothing_if_false()} , since you probably do not want {a=>"b"} if both are false, and {a=>1,b=>1} if both are true. At least if f1 returns true and f2 returns false, or vice versa, you will get a warning about odd sized hash.

The programmer calling a function can control whether it is evaluated in list or scalar context, but she may not be able to control whether it returns true or false.

IMHO, if you do 1/(), (1)/(), or 1/undef you cannot safely call such functions in array context, like building keyword arguments foo(kw=>boolfunc(),...) or foo({kw=>boolfunc(),kw2=>...},...). Not without having to scatter !! or 0+ all over.

---+ 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:

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

  2. 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

  1. 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.

If you are using the 1/0 scheme, don't say return $a < $b, instead say return 0+($a < $b)

If you are calling code that uses a different Boolean scheme (or, possibly, no consistent scheme), convert to the Boolean scheme that you use in your code, using operators such as

  • !! to normalize a standard Perl 1/0!! Boolean
  • 0+ or 1* to convert to a more portable 1/0 boolean from a standard Perl 1/0!! boolean
  • ?: and all the rest of Perl's arsenal for undefs and strings that may or may not want to be considered false or failures

If looking at the return value of a function that returns a ref or undef

  • If 1/!!0 Perl-like Booleans, say return !!ref_retval_func() or defined ref_retval_func()

  • If 1/0 more portable Booleans, say return 0+!!ref_retval_func() or 0+(defined ref_retval_func())

Way too much detail below.

---++ 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.

---++ 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.

---+ EXCESSIVE MESSY DETAIL

---++ Discussion elsewhere about Perl's many values of truth

Posts such as What do Perl functions that return Boolean actually return and Why does Perl use the empty string to represent the boolean false value? discuss what Perl boolean functions and operators actually return. Basically special values, whose behavior is specified by the Perl manuals.

@cim links to the perl manuals: http://perldoc.perl.org/perlsyn.html#Truth-and-Falsehood

Truth and Falsehood

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.

Similarly http://perldoc.perl.org/perlop.html#Relational-Operators

Relational Operators

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.

Unfortunately, the accepted answer for What do Perl functions that return Boolean actually return discusses the internals, but then recommends

my $formatted = $result ? '1' : '0';

which is back to where we started.

@amon shows us the light (!!) in a comment on the question What do Perl functions that return Boolean actually return

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

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

---++ Creating a predicate from value / undef

Similarly, you may be tempted to take a function such as

sub find_string_in_table {
   # returns string value if found, undef if not found
   return $lookup_table->{$_[0]}; 
}

and refactor it to a predicate

sub is_string_in_table {
   return find_string_in_table(@_);
}

and then later evolve to, perhaps, have a sanity check or performance optimization.

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return find_string_in_table(@_);
}

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.

sub is_string_in_table {
   return !!0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return (defined find_string_in_table(@_));
}

or if you are using 1/0 Booleans

sub is_string_in_table {
   return 0 
       # don't even bother for long strings
       if 1000000 < length($_[0]);
   return 0+(defined find_string_in_table(@_));
}

If instead of find_string_in_table it was find_object_ref_in_table, you might do just return 0+!!find_string_in_table(@_) because you would not need to worry about strings like q() and "0".

If you want boolean functions in code you write to behave like native Perl operators return (!!1) for true and (!!0) for false.

I.e. 0/1, but logically negate twice using the ! operator to convert your 1 or 0 into Perl's 'native' boolean.

e.g.

sub my_boolean_function {
      ... 
      return !!1; # true 
      ...
      return !!0; # false
}

**---+ 0/1 --> 1/!!0 conversion **

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:

  • ref / undef : for functions that return a typical http://perldoc.perl.org/perlobj.html object, a ref to a blessed hash or other type. Return undef on error, not found, etc.

  • 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.

I have tried both "return;" and "return();"

Consider

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a {if(@_) {return 1} else {return ()}}'
Use of uninitialized value in concatenation (.) or string at -e line 1.
a()=<<>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return @_ } '
a()=<<0>>

%  perl -wle 'print "a()=<<".a().">>\n"; sub a { return !!@_ } '
a()=<<>>

%

---+ BOTTOM LINE

Use 1/0 (printable and portable), 1/0!! (most like Perl's native boolean functions).

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.

Finally, if you have ever done

$>  perl -wle 'print false && true'

you may have received

Unquoted string "false" may clash with future reserved word at -e line 1.
Unquoted string "true" may clash with future reserved word at -e line 1.
Bareword found in conditional at -e line 1.
true

so it appears likely that some day Perl may have an "official" scheme for Booleans, with values true and false.

I wonder how those will behave?

Formwork answered 17/9, 2016 at 1:15 Comment(9)
Hi. I don't program in Perl, but I performed a very simple acid test on your proposed "native" !!0: namely, I evaluated print !!0, "\n";. Perl didn't print anything; just what appears to be a blank line. A false object which doesn't have a positive, non-blank printed representation consisting of some visible characters is a poor choice to recommend. I also tried printing !!1 the same way; that renders as just 1. (So is 1 the native true? Or is it some object other than 1 which looks like it when printed?) Maybe you should just stick with 1 and 0, and not be too clever.Uveitis
@Uveitis Perl's comparison operators return a value that stringifies to the empty string: print 1 == 2. So there's already a precedent for this built into the language.Tarmac
@Kaz: coincidentally I added a recommendation to use (1*$perlish_boolean) to convert from Perl's 1/special empty string to the more familair to non-Perl programmers 1/0.Formwork
@Kaz: by the way, I agree with you: every value should have a visible default print format. But what about the empty string? IMHO every language should have a print function that can make every value visible. It's just that, in Perl, the `print' function is not that nicely behaved make-every-value-visible function.Formwork
search.cpan.org/dist/perlsecret/lib/… search.cpan.org/dist/perlsecret/lib/perlsecret.pod#Bang_bangIncredulous
@xxfelixxx: thanks. I had seen Perl secret.com, and even !!, but I had not realized WHY it should be used. Basically, return !! your truth value if you want to be compatible with Perl's native Boolean ops; and 1* your truth value if you want to be more C / C++ like. And avoid functions that contain a mix of { return $a<$b;... return 1; return 0; return undef; return ''; return "0 but true"...}Formwork
I started off writing C in Perl, or, rather, the 1/0 subset. I must admit that I played around with return "1".__FILE__.':'LINEFormwork
I would strongly advise against the use of !! in production code as it reduces comprehensibility. I have seen too much wtf code from programmers who like to sprinkle !! everywhere....Incredulous
@xxfelixxx: I agree with you. In my first job I faced this issue a lot. See: https://mcmap.net/q/462628/-use-of-double-negation-duplicateDentation

© 2022 - 2024 — McMap. All rights reserved.