in reply to Use of wantarray Considered Harmful

I'm undecided on the issue. Consider the code I've seen on PerlMonks this week:

$field = $sth->fetchrow_array();

The result of fetchrow_array in scalar context is undefined or at least undocumenteddocumented as undefined, at least some of the time. What should fetch_array do in this situation? Let the result be undefined (GIGO), or provide something sensible? The latter requires wantarray (or at least a commitment to using a compatible op).

Given a "$scalar = x()", I expect that x() returns a scalar. I know very well that's not necessarily true

It is necessarily true. x() cannot return anything but a scalar in scalar context.

In scalar context
sub x { ...; @x }@x evaluates to the num of elements in @x, so
x() returns the num of elements in @x
sub x { ...; ($x,$y) }($x,$y) evaluates to $y, so
x() returns $y
sub x { ...; () }() evaluates to undef, so
x() returns undef

Replies are listed 'Best First'.
Re^2: Use of wantarray Considered Harmful
by kyle (Abbot) on Dec 12, 2008 at 21:34 UTC
    $field = $sth->fetchrow_array();

    As you say, the result is not documented. Some would say this is just a bug, like using length to get the length of an array.

    Something sensible in this case could use wantarray, as in:

    sub fetchrow_array { my @out; # ... if ( wantarray ) { # list return @out; } elsif ( ! defined wantarray ) { # void return; } elsif ( scalar @out <= 1 ) { # scalar return $out[0]; } else { # scalar die 'too many return values for scalar'; } }

    That seems safe and yet still not quite satisfactory. I'm not sure I'd want to have some code that worked suddenly fail because one valid query (returning one field per record) changed to another valid query (returning many fields per record).

    Given a "$scalar = x()", I expect that x() returns a scalar. I know very well that's not necessarily true

    It is necessarily true. x() cannot return anything but a scalar in scalar context.

    I should have said something like, "I know very well it might return more than one value in some other context." My point was that, seeing "$scalar = x()" implies to me that x() only returns one value, ever, in any context. I would ordinarily think that "@array = x()" would give me an array with one element. Using wantarray allows x() to violate that expectation, often for no good reason.

    I think a case can be made for context-sensitivity in DBI::fetchrow_array, and probably lots of other places. I'll say again, however, I think those cases are still very rare.

      Some would say this is just a bug, like using length to get the length of an array.

      While it is a bug (if only a documentation bug), I don't see the parallel or your point. The behaviour of length(@a) is documented, and the result is incorrect for all but arrays with one element. The behaviour of $field = $sth->fetchrow() isn't documented and it actually worked (if I remember correctly).

      I think a case can be made for context-sensitivity in DBI::fetchrow_array

      Really? What argument can be made for fetchrow_array that can't be made for other methods in general? In fact, given the name of the function, fetchrow_array, it seems to me to be the least likely of candidates.

      I think I disagree with your suggestion that $scalar = x() should imply anything about how that function behaves in list context. You get the same issue without wantarray. For example,

      my @arr = qw/1 2 3 4 5 6 7 8 9/; sub x { return @arr; }

      Now, $scalar = x() returns the length of the array. @list = x() returns the actual array. There's no wantarray in sight, but we still get different behavior in different contexts.

      Granted, wantarray can be used to generate hard-to-understand software. Then again, so can almost any feature of every language out there.

      G. Wade
Re^2: Use of wantarray Considered Harmful
by ysth (Canon) on Dec 14, 2008 at 03:15 UTC
      It's documented to be undefined, so I'm covered, but thanks for the clarification. I thought I had checked and found nothing. I fixed the post to which you replied.