Home arrow Perl Programming arrow Page 4 - Perl Subroutines: Arguments and Values

Contextual Return Values - Perl

In this second part of a three-part series covering subroutines in Perl, you will learn about missing arguments, default argument values, and more. It 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. Perl Subroutines: Arguments and Values
  2. Default Argument Values
  3. Scalar Return Values
  4. Contextual Return Values
By: O'Reilly Media
Rating: starstarstarstarstar / 5
August 23, 2007

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement


Make list-returning subroutines return the “obvious” value in scalar context.


There is only one kind of list in Perl, so returning in a list context is easy—you just return all the values you produced:

  sub defined_samples_in {
      return grep {defined $_} @_;
  }

But what should that subroutine return in a scalar context? It might legitimately return an integer count (like grep itself does), in which case the subroutine stays exactly the same:

  sub defined_samples_in {
      return grep {defined $_} @_;
  }

Or it might instead return some serialized string representation of the list (like localtime does in scalar context):

  sub defined_samples_in {
      my @defined_samples = grep {defined $_} @_;

      # Return all defined args in list context...
     
if (wantarray) {
          return @defined_samples;
      }
      # Otherwise a serialized version in scalar context...
     
return join($COMMA, @defined_samples);
  }

Or it might return the “next” value in a series (like readline does):

  use List::Util qw( first );

  sub defined_samples_in {
     
# Return all defined args in list context...
     
if (wantarray) {
          return grep {defined $_} @_;
      }

      # Or, in scalar context, extract the first defined arg...
     
return first {defined $_} @_;
  }

It might try to preserve as much information as possible and return the full list of values using an array reference (which no Perl 5 builtin does):

  sub defined_samples_in {
      my @defined_samples = grep {defined $_} @_;

      # Return all defined args in list context...
     
if (wantarray) {
          return @defined_samples;
      }
     
# Return all defined args (indirectly) in scalar context...
     
return \@defined_samples;
  }

It might even give up in disgust (like sort does):

  sub defined_samples_in {
      croak q{Useless use of 'defined_samples_in' in a non-list context}
          if !wantarray;

      return grep {defined $_} @_;
  }

Perl’s list-returning builtins don’t have a consistent behaviour in scalar context. They try to “do the right thing” on a case-by-case basis. Mostly they get it right; the scalar context results of grep, and localtime, and readlineare what most people expect them to be.

Unfortunately, they don’t always get it right. The scalar return values ofselect,readpipe,splice,unpack, and the variousget...functions can be surprising to infrequent users of these functions. They have to be either memorized or repeatedly looked up in the fine manual. For many people, this makes using those builtins harder than it should be.

Don’t perpetuate those difficulties in your own development. If you’re writing a library of subroutines, make them predictable. Make every list-returning subroutine return the “obvious” value in scalar context.

What’s the “obvious” value? It’s the value that the developers who use the subroutine actually expect it to return. For example, if they all use defined_samples_in() like so:

defined_samples_in( ) like so:What’s the “obvious” value? It’s the value that the developers who use the subroutine actually expect it to return. For example, if they all use defined_samples_in( ) like so:

  if ( defined_samples_in(@samples) > 0 ) { 
     
process(@samples);
  }

then they obviously expect it to return a count of defined samples. So the “obvious” scalar context return value is that count.

On the other hand, if everyone uses it like this:

  my $floor_samples_ref         = defined_samples_in(@floor_samples);
  my $restocked_samples_ref     = defined_samples_in(@restocked_samples);

  # and later...

  swap_arrays($floor_samples_ref, $restocked_samples_ref);

then the expectation is clearly that the subroutine returns a reference to the array of results. So that’s the “obvious” scalar return value.

In other words, the “obvious” return value in a scalar context is whatever the people who use your code think it’s going to be (before they read the fine manual). That definition of obviousness presents a dilemma, though. The way you work out whether your proposed scalar-context behaviour is obvious is by implementing it and seeing how many people it trips up. But once the subroutine is deployed and client code is relying on it, it’s too late to change its return value if that value turns out not to be what most people expect.

The solution (which is discussed in greater detail in Chapter 17) is to “play test” the subroutine before it’s deployed. That is, ask the people who will actually be using your subroutine what they expect it will do in scalar context. Or, better yet, have them write sample code that uses the subroutine, and see how they use it. If you get a consensus (or even just a simple majority opinion), implement that. If you don’t get agreement on a single “obvious” behaviour, see the “Multi-Contextual Return Values” guideline later in this chapter.

Unfortunately, getting this kind of preliminary feedback isn’t always feasible. In such cases, you should simply select a reasonable default, based on the three fundamental categories of list-returning subroutines: homogeneous, heterogeneous, and iterative.

A homogeneous list-returning subroutine is one that returns a list of data values that are all of a single type: a list of samples, a list of names, or a list of images. Perl’s built-inmap,grep, andsortare examples of this type of subroutine. Because no one value in a homogeneous list is more significant than any other, the only interesting property of the list in a scalar context is usually the number of values it contains. Hence, in scalar contexts, homogeneous subroutines are usually expected to return a count, asmapandgrepboth do.

A heterogeneous list-returning subroutine is one that returns a list containing distinct pieces of information: name, rank, and serial number; account number, account name, and balance; year, month, day. For example, thestat,caller, andgetpwentbuiltins are all heterogeneous. The lists returned by subroutines of this type often do have a single piece of information that is more significant than any other, and they’re typically expected to return that value in scalar contexts. For example,callerreturns the caller’s package name, whilstgetpwentreturns the relevant username.

Alternatively, all of the information returned by a heterogeneous subroutine might be equally important. So this type of subroutine is sometimes expected to return some kind of serialized representation of that information in scalar context, aslocaltimeandgmtimedo.

An iterative list-returning subroutine is one that returns an iterated series of values, typically the result of successive input operations. The builtinsreadlineandreaddirwork this way. Iterative subroutines are always used for stepping through sequences of data, so in a scalar context, they should always return the result of a single iteration.

Remember, though, that these suggested default behaviours are recommendations, not natural laws. You may find that your “play testing” suggests that some other return value is more appropriate—more expected—in your particular subroutine. In that case, you should implement and deploy that behaviour instead, and then explicitly document the reasons for your choice.

Please check back next week for the conclusion to this article. 



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