In A Tiny Code Quiz, Ovid posted this snippet of code inspired by Ben Tilly:
@ar1 = qw(foo bar);
@ar2 = qw(a b c d);
print scalar (@ar1, @ar2);
The answer jumped out at me, but that may be because I spent a lot of time documenting how context works in Perl. The book argues that there are two distinct contexts in Perl. Amount context governs how many items an expression expects (zero, one, or many) and type context governs the nature of data an expression expects (a true or false value, a number, unstructured data).
I know that it sounds like adding a post hoc formalism to a language deliberately designed to break the shackles of rigid mathematical thinking on programming language design and usability, but my thesis is that explaining how Perl works in terms of these two contexts helps people write better code because they're prepared to read and comprehend the documentation.
If you browse PerlMonks or, back when Usenet existed, comp.lang.perl.misc, you've probably heard the phrase "no such thing as a list in scalar context". That's one of the worst explanations to offer to a Perl novice, because it's simultaneously accurate and incomprehensible. What we should say instead is "the comma is an operator".
What's a Perl Operator?
In Perl, an operator is an action. It's a verb, if you're linguistically adept. It's not a variable, because it doesn't have data attached. It's not a value, because it doesn't change. (It's not a function or a method, because Perl's parser knows about it. It's a special kind of verb, a verb that Perl programs are born knowing. Take that, Sapir-Whorf and your thousand words for snowclones!) This seems counterintuitive, like arguing that the semicolon which separates expressions is an operator (even though good Haskell tutorials occasionally suggest that if they have really good explanations of monads). It seems wrong, but it's true.
Every Perl operator has several characteristics. These include arity (how many pieces of data it expects), precedence (when Perl should evaluate it with regard to other operators in an expression), associativity (whether it evaluates its data leftmost or rightmost), and its fixity (where it appears in an expression with regard to its data). All of those characteristics apply to the comma operator. When you write:
my ($rank, $rate) = ( 'Senior Consultant', '1000 septim per hour' );
... you expect the code to have the same effect as:
my $rank= 'Senior Consultant';
my $rate = '1000 septim per hour';
Even if you never thought about the comma as an operator, you probably expect that it separates expressions, appears between two operands, and operates left to right. If you've read Perl's precedence rules, you probably also expect that other operators get evaluated first. (That's why the parentheses are necessary.)
You may have never thought bout the context of the comma operator.
The Context of the Comma Operator
Some operators enforce context on their operands. scalar
is a
good example. You can't say what @array
will evaluate to without
knowing the surrounding context. When used as the first operand to
push
, Perl treats the array as a container and adds elements to
it. When used as an argument to a (prototypeless) function, Perl extracts the
elements of the array into a list. When evaluated in scalar context, you get
the number of elements of the array.
(If you don't know much Perl, this may sound really complicated, but you have to learn a few things about fixity and arity and precedence in any language that isn't completely consistent about its representation form. Even Common Lisp has special rules for quoting that complicate things. If you want a programming language that people who don't know can read and instantly understand everything about what it's doing and why, you're going to be very disappointed when you turn off Star Trek and go outside.)
Other operators pass through their contexts. These operators
include return
and the comma operator. In other words, when Perl
evaluates the operands of the comma operator, it does so with respect to the
context in which it's evaluating the expression containing the comma
operator.
In other words, in the expression:
my ($rank, $rate) = ( 'Senior Consultant', '1000 septim per hour' );
... the comma operator here is evaluated in list context because the lvalue of the assignment operator imposes list context (assigning to two variables), so the comma operator produces a list of two literal strings.
In the expression:
my $rate = ( 'Senior Consultant', '1000 septim per hour' );
... $rate
gets the second literal, because the comma operator
evalutes all of its operands and, in scalar context, evaluates to its right
operand.
In the expression:
my @odds = ( 1, 3, 5, 7, 9 );
my @evens = ( 2, 4, 6, 8, 10 );
my ($odd, $even) = ( @odds, @evens );
... $odd
contains 1
and $even
contains
3
, because the comma operator evaluates in the list context
imposed by the assignment to an lvalue list. The comma operator flattens its
two operands into a list and the first two elements of that list—the
first two elements of @odds
—get assigned to the two lvalue
variables.
In the expression:
my @odds = ( 1, 3, 5, 7, 9 );
my @evens = ( 2, 4, 6, 8, 10, 12 );
my ($odd, $even) = scalar ( @odds, @evens );
... $odd
contains 6 and $even
remains undefined, because the comma operator evaluates its operands in the scalar context imposed by the scalar
operator. In scalar context, of course, the two arrays each evaluate to the number of elements they contain.
Keep in mind, however, that the comma operator also has a behavior in scalar
context. In scalar context, it evaluates to a single value, that of its right
operand. The @evens
array contains six elements, so that's what
the comma expression evaluates to.
Understanding context helps—but the real problem is that most of us never think of the comma operator is an operator. Very few Perl tutorials and examples go into detail about how the parts of Perl fit together with regard to Perl's design philosophy of context, and very few really talk about what operators really are. When understand these concepts, the code example should make a lot of sense to you (even if you'd never write such a thing).