|Perl: the Markov chain saw|
If you believe in Lists in Scalar Context, Clap your Handsby gone2015 (Deacon)
|on Oct 23, 2008 at 17:44 UTC||Need Help??|
There was a learned discussion around this recently 715269 -- from which one concluded... well I wasn't sure...
The humble programmer needs a mental model for what goes on, and some way to describe it. So I wondered, what's wrong with the notion of a List in Scalar Context ?
The business of Context is peculiar to Perl, it's easy to miss first, second and possibly third time through. Bearing that in mind, this is what perldata says:
In a context not requiring a list value, the value of what appears to be a list literal is simply the value of the final element, as with the C comma operator. For example,So, blink and you've missed a number of things: ...first, the implication that a list (or at least a list value, or possibly a list literal) is not the same as an "actual array"; ...second, we can talk about an "array in scalar context" (so a "list in scalar context" seems natural); and...third, the author is in knots describing something that looks like a list, but isn't a list (and I want to meet the person who understood the fineness of that distinction the first time they read it).assigns the entire list value to array @foo, but
But the notion of a list goes well beyond list literals, and we need a way to think about how lists are handled more generally.
You might think that lists are something to do with '()' as list constructor and ',' as list item separator. (Anonymous lists have the '' constructor...) Of course you would be wrong. The '()' are incidental, required because ',' is a sort of operator with lower precedence than '=', in particular. The '()' are not required at all in some contexts, notably following a list operator (eg print !).
Anyway, in the following, the stuff to the right of the '=' appear to be lists:
so what happens if we change this to assignment to a scalar, so we have Scalar Context:
Case 3 we are asked to understand in terms of the Scalar Context behaviour of the ',' operator, but can equally well be understood as a List in Scalar Context. Case 4 can only be understood in terms of the Scalar Context behaviour of '..' (in this case to whine about the arguments). However, all the other cases are consistent with the notion that there is a list on the rhs, and that in Scalar Context the result is the last item of the list -- and not a ',' operator in sight ! The List in a Scalar Context is holding up, so far...
Of course, we entirely understand that an Array or a Hash in Scalar Context is quite different:
The List in a Scalar Context notion might suggest that case 3 would set $r to the last entry in @r and case 4 would set $v to some value in %r, if you thought that '()' was a list constructor -- but it isn't, so it doesn't. So far so good, but what about:
If we think about the rhs as a list, we may be disappointed -- in List Context, the @r or %r would be interpolated. That's not what happens here: the @r and %r are evaluated in Scalar Context, first; the result is then consistent with either List in a Scalar Context or Comma as a Scalar Operator views. (Remembering that ',' is just a list item separator, it's not a list constructor, and it has low precedence anyway.)
OK. The mental model so far, in expressions:
Now, subroutines: we have to understand that Context has a long arm -- the Context in which a subroutine is called, will, at a distance, apply to any expression yielding a return value. And different calls may provide different Contexts. This is slippery stuff, but at least the results are entirely consistent. If you take the examples above and replace each rhs by a call to a subroutine which returns that rhs, then the results are identical. But, it is worth noting that, for example, replacing:
changes the Scalar Context result in a way that may slip your mind as you make an apparently straightforward change !
This is telling us something. A subroutine that is serious about being called in either Scalar or List Context, must ensure that it returns an explicit, well defined result in both cases -- which for Scalar may be the same as Array in a Scalar Context, or List in a Scalar Context, or anything else it cares to return. Further, it is foolish to call a subroutine in a Context which it does not explicitly support -- which implies that it is foolish to call a subroutine defined only to return a list, in a Scalar Context, and expect List in a Scalar Context semantics.
This is also true of Perl functions. There is no point assuming that a Perl function that returns a list in List Context will do anything useful at all in Scalar Context, except where it is defined to do so.
So, after all this (and thank you for reading this far!) am I clapping my hands ?
Well, yes I am. It seems to me that List in a Scalar Context is a perfectly good way of understanding the behaviour of lists in expressions, as discussed above.
However, the application is limited. In particular, it cannot be used to predict what a list returning operation will do if you decide to use it in Scalar Context. To that extent, the problem with the notion of List in a Scalar Context is that it is deceptively general, and dangerously so.
My conclusion: List in Scalar Context is a more general notion than Comma in Scalar Context, but you have to understand its limitations. And, it is much more important to understand that knowing that a Perl function or a subroutine returns a List in List Context, tells you nothing useful about its Scalar Context behavious.
I shall continue to clap, but in a quiet and restrained manner, as befits someone tip-toing through the minefield that is Perl Semantics !
BTW: I'm gagging to know how to describe why this:
does what it does.
Finally, and for extra points (and points mean prizes), how does one describe the difference between a list and an array ?