Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

Using wantarray, a sub can tell what context it was called in so it can behave differently depending on whether its return value will be ignored, put into a scalar, or put into a list. An idiom I've seen a number of times uses wantarray to decide whether to return an array or an array reference.

sub get_x { my @x = qw( stuff ); return wantarray ? @x : \@x; }

This way, you can either write "@foo = get_x()" or "$foo = get_x()", and it will "just work".

I am against this practice.

Which way "just works"?

At $work right now, you can find both of these:

return wantarray ? @x : \@x;
return wantarray ? @x : $x[0];

I wonder what will happen when one of those two programmers has to work on the other's code.

I didn't think context mattered

...or I didn't know I changed the context.

Imagine refactoring this:

my $dewdrop_description = get_droplet(); $dewdrop_description->{substance} = 'water'; $dewdrop_description->{molecular_array} = get_moles( 'water' ); $dewdrop_description->{temperature} = 37;

We want to eliminate the repetition. Make it like this:

my $dewdrop_description = { %{ get_droplet() }, substance => 'water', molecular_array => get_moles( 'water' ), temperature => 37, };

Nice, right? Yes, except now it's broken because at the end of get_moles() we have:

return wantarray ? @moles : \@moles;

Our straight-forward refactoring has changed the context of get_moles() from scalar to list, and the result is that $dewdrop_description gets polluted with all manner of extra stuff. If you're lucky, get_moles() returns an even number of items, and you have warnings on, and you are told "Odd number of elements in anonymous hash". If you're not lucky, there's no warning, and an odd index element from get_moles() clobbers a key that get_droplet() returned.

Don't surprise me

I'm often looking at code already written for some example of how to use things. Given a "$scalar = x()", I expect that x() returns a scalar. I know very well that's not necessarily true and that context can have effects far beyond its source. Nevertheless, it generally does not occur to me that maybe this scalar-returning behavior is conditional.

A sub that returns more than one value can have unpredictable behavior in scalar context. What you get depends on whether the scalar context applies to an array or a list. If a sub returns a list, you can't just apply a scalar context to it and know what it will do without looking inside the sub to see what's to the right of return. In light of this, it almost seems merciful to use wantarray to lay down the law. A consistent use across code might actually clear some things up.

Still, I am not in favor of this.

Be consistent

Always return the same thing.

If your caller wants to stick your list in a scalar instead of an array, make it build the array reference itself.

If the list you return is so massive that copying it all is a burden, return an array reference. If the caller wants to copy it into an array anyway, it can dereference your return value and suffer the consequences.

On the defense

I've seen it suggested that every assignment from a sub call should be with a list context.

my ($scalar) = why();

This way, if it's giving you a scalar, you get it. If it's giving you a list, you get the first element. If it's giving you an array, you get the first element. It's about as consistent as you can get without looking into why().

I'm not quite to that point yet, but I can see where it's coming from.

A good use of wantarray

I think it's a good idea to use wantarray to check for void context. That can be used for run-time programming mistakes like calling an accessor in void context.

sub get_or_set { my $self = shift; if ( ! @_ && ! defined wantarray ) { die "accessor called in void context"; } $self->{stuff} = shift @_ if @_; return $self->{stuff}; }

This will catch the case where you don't know you've passed in nothing.

my @x = (); # but I thought it was qw( foo ) ! get_or_set( @x ); # set value

"Better" versions of wantarray

I'll note without comment that there are a couple of modules out there that do what wantarray does and more, if you're into that kind of thing.


In reply to Use of wantarray Considered Harmful by kyle

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (9)
As of 2024-04-18 11:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found