in reply to Re^2: Evil Interview Questions
in thread Evil Interview Questions

This reads to me like it is motivated by the all-too-common and deceptively flawed meme of "an array in scalar context gives its size while a list in scalar context returns its last element". I've seen that used to justify so very many flawed conclusions. You can also use it to make flawed justifications to quite a few "correct" conclusions, but that just demonstrates how the flaws in that meme are deceptive (which is probably part of why it is still "all too common").

The subroutine does /not/ return an array that then decides to give its size when it finds itself in a scalar context. There are many other ways that thinking "this sub returns an array" will mislead people. So it is better to divorce yourself from both of these flawed ways of thinking about context in Perl.

And, no, my objections are not based on some secret knowledge on how Perl works internally. They are based on repeatedly seeing people make mistakes about how Perl scripts would behave based on these ideas. There are lots of cases where these ideas give the right answer. But the cases where they lead to the wrong answers are surely more important.

- tye        

Replies are listed 'Best First'.
Re^4: Evil Interview Questions (memes)
by kyle (Abbot) on Feb 09, 2008 at 18:22 UTC

    This reads to me like it is motivated by the all-too-common and deceptively flawed meme...

    I think I know better than that (but I can see how someone reading might think otherwise).

    sub get_array { my @x = (4, 5, 6); return @x; } sub get_list { return (10, 11, 12, get_array()); } my @as_array = get_list(); my $as_scalar = get_list(); print "last list item: $as_array[-1]\n"; print "list in scalar: $as_scalar\n"; __END__ last list item: 6 list in scalar: 3

    Not only that, the context extends to everything in a list after return:

    sub context_in_position { my ($position_name) = @_; my $wa = wantarray; my $context; if ( $wa ) { $context = 'list' } elsif ( defined $wa ) { $context = 'scalar' } else { $context = 'void' } print "context in position '$position_name': $context\n"; return; } sub list { return ( context_in_position( 'a' ), context_in_position( 'b' ), ); } print "list() in void:\n"; list(); print "list() in scalar:\n"; scalar list(); print "list() in list:\n"; () = list(); __END__ list() in void: context in position 'a': void context in position 'b': void list() in scalar: context in position 'a': scalar context in position 'b': scalar list() in list: context in position 'a': list context in position 'b': list

    I think some Perl programmers might find it pretty surprising that in an expression like ( foo(), bar() ), foo() would find !wantarray(), but that's what happens.

    Do you still think I'm missing something here? Really, I want to get this straight, conceptually.

    The subroutine does /not/ return an array that then decides to give its size when it finds itself in a scalar context.

    I've heard the news that the array finds itself in a scalar context even before it makes it "out the door" to the wild world outside the sub. The context that sub is in seems to extend into the sub, into the place where the return "happens."

    That said, it seems blindingly obvious that return @this returns an array! So, here's a question for you. Is there any way to explain what happens, consistently and accurately, if we accept the statement that "return @this returns an array"? I think such an explanation would be far more readily accepted and understood than an explanation that starts with "you can't return arrays."

    (As an aside, it is likewise obvious that return IO::File->new() returns "a new IO::File object", but this fits nicely in the "scalar, list, or Nothing" idea because the object is a scalar. But would we say that an array is a list or a scalar? And if we say that an array is a list, how do we explain how it behaves differently from a list in scalar context?)

    There are lots of cases where these ideas give the right answer. But the cases where they lead to the wrong answers are surely more important.

    I'd be very interested to read some of the cases you refer to.

      That said, it seems blindingly obvious that return @this returns an array!

      Is it blindly obvious--really? Would you also say that return &foo obviously returns a subroutine?

      return @array doesn't return an array any more than return &foo returns a subroutine. The key is that return EXPR doesn't make the subroutine/eval/do FILE return the expression itself, but rather the evaluation of the expression, which may depend on the context in which the subroutine/eval/do FILE is evaluated.

      But would we say that an array is a list or a scalar?

      You're focusing on what an array is in different contexts. An array is an array. If you focus on what an array evaluates to you'll get more to the point. Every evaluation is in a context, and an array evaluates differently in list versus scalar context just as a subroutine (may) do. perldata says (my emphasis)

      Note that the value of an actual array in scalar context is the length of the array
      i.e., it evaluates to the length.

      lodin

        Excellent! I think that finally explains it.

        If you focus on what an array evaluates to you'll get more to the point.

        Whatever expression return will be evaluated in the context of the sub's call (as described by wantarray), and passed out that way.

        Would you also say that return &foo obviously returns a subroutine?

        No, but I don't consider "&foo" to be a passable data structure. The only time it isn't a call to a sub is (1) if it has "\" in front of it (making a reference), (2) if it has defined or exists in front of it, and (3) some other surprise I haven't discovered yet.

        sub foo { die 'call to foo' } print 'foo is ', defined &foo ? 'defined' : 'not defined', "\n"; print 'bar is ', defined &bar ? 'defined' : 'not defined', "\n"; __END__ foo is defined bar is not defined

        Many thanks for your reply. I do believe you have penetrated my concrete cranium.

      return $foo; returns the value in $foo. It doesn't return the $foo variable. return bar(); returns the value(s) returned by bar(). So return @array; returns the values from @array. return $foo ? $bar : $baz; doesn't return a ternary operator. return returns a list of zero or more (scalar) values. Always.

      But would we say that an array is a list or a scalar?

      An array is a (type of) list, sure. Alternately, an array is something that holds a list such that you can manipulate the list.

      And if we say that an array is a list, how do we explain how it behaves differently from a list in scalar context?

      There's that broken meme again. There is no particular way that a "list" behaves in scalar context. Any number of ways of producing a list of scalar values (which is the "list" that an array contains) can have any number of different behaviors in a scalar context. A "list literal" aka "use of the comma operator, usually between parens", evaluates all but the last expression in void context and then evaluates the last expression in scalar context and returns the resulting scalar. A "list literal" doesn't even return the last value. So the answer to "What does a list return in a scalar context?" is, of course, "Mu!".

      The term "list" can mean quite a few quite different things when talking about Perl. The two most common are "list of scalar values" and "list literal" (use of comma). So it makes little sense to talk categorically about what a "list" does.

      The dicotomy "array or list" is a false one in Perl, despite it being fairly widely ascribed to. An array is a list.

      - tye        

        That's illuminating. Thanks!

        Still, there are questions.

        A "list literal" aka "use of the comma operator, usually between parens", evaluates all but the last expression in void context and then evaluates the last expression in scalar context and returns the resulting scalar.

        The second code block in Re^4: Evil Interview Questions (memes) has what I think is the scenario you describe, but at no point does any item in the list have a different context from any other item. Can you demonstrate the behavior you describe here?

        Any number of ways of producing a list of scalar values (which is the "list" that an array contains) can have any number of different behaviors in a scalar context.

        Can you expand on this? I'm not sure what you mean by "ways of producing a list of scalar values" or what the "any number of different behaviors" would be.

        Thanks again for your remarks.