Home arrow Perl Programming arrow Page 4 - Returns and Perl Subroutines

Returning Failure - Perl

In this final part of a three part series covering subroutines in Perl, we will discuss returns and return values, as well as prototypes. This article is excerpted from chapter nine of the book Perl Best Practices, written by Damian Conway (O'Reilly; ISBN: 0596001738). Copyright © 2006 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

TABLE OF CONTENTS:
  1. Returns and Perl Subroutines
  2. Prototypes
  3. Implicit Returns
  4. Returning Failure
By: O'Reilly Media
Rating: starstarstarstarstar / 4
August 29, 2007

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement


Use a bare return to return failure.


Notice that each final return statement in the examples of the previous guideline used a return keyword with no argument, rather than a more-explicit return undef.

Normally, relying on default behaviour is not best practice. But in the case of areturnstatement, relying on the default return value actually prevents a particularly nasty bug.

The problem with returning an explicit return undef is that—contrary to most people’s expectations—a returned undef isn’t always false.

Consider a simple subroutine like this:

  use Contextual::Return;

  sub guesstimate{
      my ($criterion) = @_;

      my @estimates;
      my $failed = 0;

      # [Acquire data for specified criterion]

      return undef if $failed;

      # [Do guesswork based on the acquired data]

      # Return all guesses in list context or average guess in scalar context...
     
return (
          LIST { @estimates          }
          SCALAR { sum(@estimates)/@estimates;             }
     
);
  }

The successful return values are both fine, and completely appropriate for the two contexts in which the subroutine might be called. But the failure value is a serious problem. Sinceguesstimate()specifically tests for calls in list context, it’s obvious that the subroutine is expected to be called in list contexts:

  if (my @melt_rates = guesstimate('polar melting')) {
      my $model = Std::Climate::Model->new({ polar_melting => \@melt_rates });

      for my $interval (1,2,5,10,50,100,500) {
      print $model->predict({ year => $interval })
      }
  }

But if the guesstimate() subroutine fails, it returns a single scalar value: undef. And in a list context (such as the assignment to @melt_rates), that single scalar undef value becomes a one-element list: (undef). So @melt_rates is assigned that one-element list and then evaluated in the overall scalar context of the if condition. And in scalar context an array always evaluates to the number of elements in it, in this case 1. Which is true.

Oops!*

What should have happened, of course, is thatguesstimate()should have returned a failure value that was false in whatever context it was called, i.e.,undefin scalar context and an empty list in list context:

  if ($failed) {
     
return (
         
LIST   { ()    }
         
SCALAR { undef }
     
);
  }

But that’s precisely what areturnitself does when it’s not given an argument: it returns whatever the appropriate false value is for the current call context. So, by always using a barereturnto return a “failure value”, you can ensure that you will never bring about the destruction of the entire planetary ecosystem because of an expectedly trueundef.

Meanwhile, Chapter 13 presents a deeper discussion on the most appropriate ways to propagate failure from a subroutine.


* Yep, that’s the sound of alarm bells you’re hearing.

† And if that sample happens to be an integer, then $found will be assigned a numeric value, exactly as expected. It will be the wrong numeric value, but hey, at least that will make the bug much more interesting to track down.

* They’d be the “edge-cases”, except that, in this instance, they’re conceptually in the middle of possibilities.

* And here “Oops!” means: theifblock executes despite the failure ofguesstimate()to acquire any meaningful data. So, when the climate model requests a numerical polar melting rate, thatundefis silently converted to zero. This dwimmery causes the model to show that polar melting rates have absolutely no connection to world climate in general, and to rising ocean levels in particular. So mankind can happily keep burning fossil fuels at an ever-greater rate, secure in the knowledge that it has no effect. Until one day, the only person left is Kevin Costner. On a raft.



 
 
>>> More Perl Programming Articles          >>> More By O'Reilly Media
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PERL PROGRAMMING ARTICLES

- Perl Turns 25
- Lists and Arguments in Perl
- Variables and Arguments in Perl
- Understanding Scope and Packages in Perl
- Arguments and Return Values in Perl
- Invoking Perl Subroutines and Functions
- Subroutines and Functions in Perl
- Perl Basics: Writing and Debugging Programs
- Structure and Statements in Perl
- First Steps in Perl
- Completing Regular Expression Basics
- Modifiers, Boundaries, and Regular Expressio...
- Quantifiers and Other Regular Expression Bas...
- Parsing and Regular Expression Basics
- Hash Functions

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: