Returning 'false' (sometimes)

Ovid on 2006-12-15T18:50:22

In code which sometimes returns 'false', I keep seeing code like the following:

sub foo {
  return undef unless bar(@_);   # or 'return 0;'
  return $something_else;
}

That behavior needs to be documented very carefully as it has a subtle bug which people keep stumbling over. Most of the time it works:

if ( my $result = foo() ) { ... }

But what if, for example, you want to accumulate results so you decide to use an array?

if ( my @results = foo() ) { ... }

Congrats! If &foo return false, you now have a one-element array which, in this context, evaluates as true. This is very likely a bug. You can accomplish the same thing, probably bug free, if you just use a bare return:

sub foo {
  return unless bar(@_);
  return $something_else;
}

From perldoc -f return:

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 a void context.

In other words, if you wish to return 'false', a bare return will Do The Right Thing.

The only significant objection this I can recall hearing is the following:

if ( some_func(1, foo(), 2) ) { ... }

Because that's in list context, if &foo has a bare return, &foo returns the empty list and &some_func receives (1, 2). I don't see this happening too often, but you can get around it by forcing scalar context.

if ( some_func(1, scalar foo(), 2) ) { ... }

Now &some_func will receive (1, undef, 2).

Also, if you're returning from a ternary operator, you can get the same behavior with this:

return $some_val ? $some_val : ();


Opposite Bug

Smylers on 2006-12-16T00:33:50

We recently hit against a bug with this the opposite way round. The JSON module's objToJson function takes a Perl data structure and returns a Json string representation of it. But if the input is undef then it uses bare return.

The bug was that this return value was being used in the parameter list of another function, and the bare return meant there was a argument missing.

Once we'd spotted the bug it was simple to put scalar before the call to objToJson, but it was subtle. If a function is documented as always returning exactly one piece of data then it might be better if it always does that, even if that means returning undef in list context.

Re:Opposite Bug

Ovid on 2006-12-16T01:34:05

You know, I did cover that specific case in my post :)

Perl::Critic enforcement

ChrisDolan on 2006-12-16T01:32:30

Damian Conway made a similarly convincing argument in Perl Best Practices. This practice be enforced with Subroutines::ProhibitExplicitReturnUndef policy in Perl::Critic.