I'm new to perl... could someone help me understand why perl eval behave like this:
eval with array or list: pass
eval with list or do: pass
eval with array or do: return value is not the expected result, it's taking the last element.
80> eval{(1,2,3)} or do {}
Useless use of a constant (2) in void context at reply input line 1.
$res[34] = 3
81> eval{[1,2,3]} or do {}
$res[35] = [
1,
2,
3
]
82> eval{(1,2,3)}
$res[36] = [
1,
2,
3
]
83> eval{[1,2,3]}
$res[37] = [
1,
2,
3
]
CodePudding user response:
You need to start by studying Perl's concept of context.
The short version is that operators are evaluated in list, scalar or void context. The context of an operator is determined by the operator of which it is an operand.
In list context, an operator may return any number (0 or more) scalars.
In scalar context, an operator must return exactly one scalar (no more, no less).
In void context, the scalars returned by an operator are discarded.
Operators can and do and return different values depending on the context. Any operator that nominally returns multiple scalars must necessarily return something different in list context and scalar context.
Now that you've studied Perl's concept of context, I'll cover how it applies to the list/comma operator.
To do that, we'll look at these two statements.
my @a = (1,2,3);
my $s = (1,2,3);
Note that all ( )
does is change precedence like ( )
in mathematics. It doesn't result in any code. It doesn't create any kind of data structure.
In the first case, we have a list assignment operator. It evaluates its right-hand side operand in list context. This means that 1,2,3
is evaluated in list context.
In list context, 1,2,3
evaluates each item of the list in list context, and returns every scalar produced. As such, it returns 1
, 2
and 3
.
In the second case, we have a scalar assignment operator. It evaluates its left-hand side operand in scalar context. This means that 1,2,3
is evaluated in scalar context.
In scalar context, 1,2,3
evaluates each item but the last in void context, then evaluates the last item in scalar context, and returns the scalar produced by the last time. As such, it's short for do { 1;2;3 }
, and it returns 3
.
Finally, let's examine the context of each operator in the expressions you provided.
The perl debugger evaluates provided code in list context.
or
imposes a scalar context on its left-hand side operand.
[ ]
evaluates its initializaing expression in list context.
eval
propagates its context to the last statement of the block.
eval{(1,2,3)} or do {}
# Order Context Operator Produces
# ----- ------- ------------- --------
# 6 list or 3
# 5 scalar eval 3
# 4 scalar , 3
# 1 void 1 <nothing>
# 2 void 2 <nothing>
# 3 scalar 3 3
# - list do <not evaluated>
eval{[1,2,3]} or do {}
# Order Context Operator Produces
# ----- ------- ------------- --------
# 7 list or ref to array
# 6 scalar eval ref to array
# 5 scalar [] ref to array
# 4 list , 1 and 2 and 3
# 1 list 1 1
# 2 list 2 2
# 3 list 3 3
# - list do <not evaluated>
eval{(1,2,3)}
# Order Context Operator Produces
# ----- ------- ------------- --------
# 5 list eval 1 and 2 and 3
# 4 list , 1 and 2 and 3
# 1 list 1 1
# 2 list 2 2
# 3 list 3 3
eval{[1,2,3]}
# Order Context Operator Produces
# ----- ------- ------------- --------
# 6 list eval ref to array
# 5 list [] ref to array
# 4 list , 1 and 2 and 3
# 1 list 1 1
# 2 list 2 2
# 3 list 3 3
CodePudding user response:
The code snippets in the question are run in a perl debugger, so in list context.
Let's start from claryfing the context aspect of this, the crucial concept in Perl. From eval
In both forms, the value returned is the value of the last expression evaluated inside the mini-program; a return statement may also be used, just as with subroutines. The expression providing the return value is evaluated in void, scalar, or list context, depending on the context of the
eval
itself.
So if the return of eval
is assigned to a scalar then the (last) expression in the block is evaluated in the scalar context. If the eval
is or
-ed (with the return of the do
block for example, like here), it is scalar context again.
In such a case the comma operator behaves as
In scalar context it evaluates its left argument, throws that value away, then evaluates its right argument and returns that value.
While comma
is evaluating and throwing values away it acts in void context (those values aren't used for anything), and those are the warnings we see:
perl -wE'$val = eval { (7, 8, 9) }'
prints
Useless use of a constant (7) in void context at -e line 1. Useless use of a constant (8) in void context at -e line 1.
since the first two values are discarded while the last one does get used. Same for
perl -wE'eval { (7, 8, 9) } or do { 1 }'
and for the same reason, as 9
gets or
-ed with the return value of do
.
If nothing at all were to be done with this
perl -wE'eval { (7, 8, 9) }'
then we would get
Useless use of a constant (7) in void context at -e line 1. Useless use of a constant (8) in void context at -e line 1. Useless use of a constant (9) in void context at -e line 1.
So these warnings are from the comma
operator, while the context for its behavior is supplied by the context the eval
is in.
Now, if the eval
's return is used in a list context, like passed to a map
or assigned to an array, then the list context inside the block makes the comma
do
In list context, it's just the list argument separator, and inserts both its arguments into the list.
So that list (7, 8, 9
) gets orderly returned and there are no warnings.
There is one more case to clarify, with [7, 8, 9]
-- now the [ ]
are the constructor for an anonymous array so the comma
operator inside is in the list context and "inserts" its arguments into the list, which is used to build an array ref. No reason for warnings from the comma
.
The fun part: when 1
is an element of the list there is no warning about it being a useless constant etc! Like, for
perl -wE'eval { (7, 1, 9) }'
we get
Useless use of a constant (7) in void context at -e line 1. Useless use of a constant (9) in void context at -e line 1.
The reason being that 1
is (apparently) special-cased this way so that one can do things like
1 while ...
Thanks for Dave Mitchell for letting me know about why 1
is excluded from warnings.
Thanks to ikegami for informing about debugger and its list context.