There are some situations where one will commonly get a return value of true, false, or undef and each needs to be handled distinctly. Two examples off the top of my head are fork and wantarray. I'm wondering what idiom monks use for this situation since I've never quite been satisfied with my own ways.
For fork, I used to write this most often:
my $pid = fork;
die "can't fork: $!" if ! defined $pid;
if ( $pid ) {
# parent
}
else {
# child
}
With Perl 5.10, this gets a little easier.
use 5.010;
my $pid = fork // die "Can't fork: $!";
if ...
That's one less statement, and it reads neatly "fork or die".
use 5.010;
given ( fork ) {
when ( undef ) {
die "Can't fork: $!";
}
when ( !$_ ) {
# child
}
default {
# parent
my $child_pid = $_;
}
}
One thing I don't like about using given/when for this is that it's hard to rearrange the clauses. I can't say "when ($_)" because it's always true, even when $_ is false. That's a problem common to this situation, however. You can't just take "false" as false, since it might also be undef. That is, one always has to check defined before falsehood.
About all this has going for it is I only create a $pid variable where I need it (in the parent). This seems clumsy for fork, but it might make more sense for wantarray.
use 5.010;
given ( wantarray ) {
when ( undef ) {
return; # void context
}
when ( !$_ ) {
return 'scalar context';
}
default {
return qw( list context );
}
}
That's good if I really need a block for each condition to take different actions in the different situations. On the other hand, if all I'm doing is selecting a return value base on context, the ternary seems ready-made.
return wantarray ? qw( list context ) :
defined wantarray ? 'scalar context';
Maybe I should have a function hide the mess and make a call to it be my idiom.
return tfu( wantarray,
true => sub { 'true' },
false => sub { 'false' },
undef => sub { 'undef' }, );
sub tfu {
my $value = shift @_;
my %todo_for = @_;
return $value ? $todo_for{ true }->() :
defined $value ? $todo_for{ false }->() :
$todo_for{ undef }->();
}
I can put my "clauses" in any order, and the sub will figure it out. I can pile into the sub defaults and validation and whatever else. That's a far cry from a convenient idiom, however.
At this point, I think I've just thought about this too much. I wonder what others think.
Re: An idiom for selecting true, false, or undef.
by perrin (Chancellor) on Mar 17, 2009 at 16:16 UTC
|
Your first way seems totally fine to me. You could use the "condition or die" approach that people use for file operations:
defined $pid or die "failed to fork: $!";
| [reply] [d/l] |
Re: An idiom for selecting true, false, or undef.
by Bloodnok (Vicar) on Mar 17, 2009 at 16:53 UTC
|
Since I discovered it (solely thanks to this community), I tend to (probably over) use autodie in cases similar to the fork example - relying on perl/the OS to make a sensible report (which, in most cases, they do) - resulting in code such as ...
use autodie;
if (my $pid = fork) {
# Some parenting wouldn't go amiss
} else {
# Do some child stuff
}
As for the simple, value based wantarray example, I quite like your tfu approach...
A user level that continues to overstate my experience :-))
| [reply] [d/l] [select] |
Re: An idiom for selecting true, false, or undef.
by ELISHEVA (Prior) on Mar 17, 2009 at 21:05 UTC
|
When I need to distinguish between the three, I generally go with
if (!defined($bSomething)) {
...
} elsif ($bSomething) {
...
} else {
...
}
and sometimes
my $x = defined($bSomething)
? $bSomething : $DEFAULT_VALUE;
if ($x) {
...
} else {
...
}
The verbosity doesn't really bother me; it works with older versions of Perl; and it's easy to read.
Best, beth | [reply] [d/l] [select] |
Re: An idiom for selecting true, false, or undef.
by xdg (Monsignor) on Mar 17, 2009 at 16:43 UTC
|
It may not be a good idiom, but usually I only check wantarray for a true value, and just return a scalar value in either scalar or void context and just let Perl throw it away if unused. I think that's probably reasonable for anything for which the typical case is to return one or more values and I don't see much point in adding the void case just to be pedantic.
For fork, the last time I did it, I used a three-part if/elsif/else for clarity.
-xdg
Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.
| [reply] [d/l] [select] |
Re: An idiom for selecting true, false, or undef.
by JavaFan (Canon) on Mar 17, 2009 at 20:35 UTC
|
I seldomly need an idiom to distinguish between all three - in most cases, I'm either interested in "true or false" or "defined or not defined". In the rare cases, the idiom I use depends whether I consider all cases equal or not.
fork is an example where not all cases are equal. undef signals an error condition, while true/false but defined happen equally often. So, I typically write:
my $pid = fork // die "fork: $!";
unless ($pid) {
... child stuff ...
exit;
}
... parent stuff ...
wait;
Note that the above code doesn't have an else clause. That's intentional.
In the rare case all three cases are equally important, I might write:
given (EXPR) {
when (!defined) {...}
when (!$_) {...}
default {...}
}
or an if/elsif/else construct. But that's so rare, I can't even remember what I did last time. In the case of wantarray, wantarray being not defined is the exceptional case - if I'm interested in it, I most likely use it for flow control, bypassing expensive calculations. | [reply] [d/l] [select] |
Re: An idiom for selecting true, false, or undef.
by zentara (Archbishop) on Mar 17, 2009 at 16:29 UTC
|
This reminds me of the Tri-State logic node going on a while ago...lost the node_id, sorry.
| [reply] |
|
| [reply] |
|
| [reply] |
|
|
|
|
|
| [reply] |
Re: An idiom for selecting true, false, or undef.
by sundialsvc4 (Abbot) on Mar 18, 2009 at 16:10 UTC
|
My best counsel on this point is: SWYMOD: Say What You Mean Or die
-
If truth-or-falsehood is known and is an appropriate response, your code should return one of those values.
-
If “nothing” is an appropriate thing to return, your code may return undef.
But, by the same logic that says (in SQL) that “NULL is not False; it is the absence of a value,” you should not return “the absence of a value” when what-you-mean is “I do have a value, and that value is False.”
-
If the situation that you have just encountered “cannot happen, but it just did,” then you should IMHO never return anything. You should die, or better yet, use one of the carp functions such as croak.
In this way, when you do “return something,” what you return is the most meaningful and descriptive response that you could have returned. And if not, you do not return at all... thus forming a useful social-contract that, if your function does return a value, that value can be relied-upon without further testing of return-values and so forth. Exception-handling blocks are efficient and cheap. (Obviously they cannot be used in all cases, e.g. for historical reasons.)
| |
|
|