Re^3: printing unitialized value of the 'do BLOCK'
by Eily (Monsignor) on Dec 17, 2019 at 15:42 UTC
|
perl -MO=Deparse,-x7 -e "if ($a) { &BLOCK }"
$a and do {
&BLOCK
};
-e syntax OK
Where if (EXPR) {BLOCK} is equivalent to EXPR and do { BLOCK } which confirms haukex's interpretation. It's worth noting that if the two forms are equivalent, the latter is probably a lot less confusing when the output value is used. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
DB<3> use strict; my $x=3; if (my $x =1) { print $x }
1
DB<4> use strict; if (my $x =1) { print $x }; print $x
Global symbol "$x" requires explicit package name (did you forget to d
+eclare "my $x"?) at (eval 13)[C:/Perl_524/lib/perl5db.pl:737] line 2.
DB<5>
but Deparse fails to cover this with -x7
>>perl -MO=Deparse,-x7 -e"if (my $x =1) { print $x }; print $x"
my $x = 1 and do {
print $x
};
print $x; # <-- no error under strict!
-e syntax OK
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] |
|
Hey!
So it means that an 'if' statement in function/RHS context returns the last expression evaluated! And this isn't documented I guess.
I guess newcomer would expect to get a 1 or ''/0 as a return value of 'if', but he can get also an 'undefined'. E.g. print do { 3 if undef };
.=
Interestingly, only one of these lines gives a warning:
print do { 3 if () };
print do { () };
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
C:\WINDOWS\system32>Perl -MData::Dump=pp -e "pp do{3 if ()} "
undef
C:\WINDOWS\system32>Perl -MData::Dump=pp -e "pp do{()} "
()
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Re^3: printing unitialized value of the 'do BLOCK' (updated)
by haukex (Archbishop) on Dec 17, 2019 at 15:27 UTC
|
I remember this particular factoid because it has bothered me in the past that the return value of if isn't more clearly documented. I also know there was an old bug report in RT (not by me) to improve the docs that I'm having trouble finding right now as well :-/ I'm kind of busy right now so I'll report back on this later.
Update: The issue I was thinking of is this one: perlsub should be more explicit when an implicit return encounters an if(){} statement, where davido suggests this text for perlsub instead:
If no return is found and if the last statement is an
expression, its value is returned. If the last statement
is an if( CONDITION ) { BLOCK } construct, the value
of the return value will come from BLOCK if CONDITION
is true, or from CONDITION if CONDITION is false.
Relying on this behavior is detremental to code legibility.
If the last statement is a loop control structure like a
foreach or a while, the returned value is unspecified.
Fixed tpyo
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
I remember this particular factiod because it has bothered me in the past that the return value of if isn't more clearly documented
Because it's not an expression. Expressions evaluate to a value, not statements. Nothing ever wants the value of an if statement.
Quote perlsub (for subs, but the same applies to do BLOCK):
If no return is found and if the last statement is an expression, its value is returned. If the last statement is a loop control structure like a foreach or a while, the returned value is unspecified. The empty sub returns the empty list.
An if statement isn't a "loop control structure", but more importantly, it's not an expression either. One shouldn't place an if statement as the last statement of a block from which we expect a value.
That said, people do use if statements that way, and the result has been quite predictable. (Update: Mostly predictable. Exception) The last expression evaluated during the course of the processing of the if statement is the resulting value.
my $x = 1;
if ($x) {
f(); # f() is the last expression evaluated.
} else {
g();
}
my $x = 0;
if ($x) {
f();
} else {
g(); # g() is the last expression evaluated.
}
my $x = 0;
if ($x) { # $x is the last expression evaluated.
f();
}
my $x = 0;
if ($x == 1) {
f();
} elsif ($x == 2) ( # $x==2 is the last expression evaluated.
g();
}
The same goes for while statements, until statements, and bare loops ({ }). Foreach loops are obviously different (since the iterator is hidden from us).
Updated
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Because it's not an expression. Expressions evaluate to a value, not statements. Of course, Perl has the oddity that a value can be expected from any statement, but you get weird results if the last statement of a sub or do block is such a statement.
The reason it bothered me is that the return value of given is explicitly documented:
When a given statement is also a valid expression (for example, when it's the last statement of a block), it evaluates to: ...
But of course given is experimental, and I thought it would be nice if it was more clearly defined for the backwards-compatible if.
One shouldn't rely on that.
I'm aware of that for for, but not for if. Source?
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
my @classifications = map {
if ($_ > 0) { 'positive' }
elsif ($_ < 0) { 'negative' }
else { 'zero' }
} @numbers;
Yes, you can write that using the ternary operator, but if/elsif/else can sometimes be clearer.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
|
Thank you for visual explanations.
I learned to use 'do { if( ... ){ ... }else{ ... } }' years ago after reading "The do Block" in the book "Intermediate Perl", Chapter 3.
Few days ago I forgot to use 'do { ...else{ } }' and got unexpected (for myself) results, later I played with minimized case which I posted here.
And, after examples and explanations, it is interesting that expression inside opposite (= unless) statement is evaluated with negation:
my $x = 1;
unless ($x) { # '$x' is NOT the last expression evaluated, but '!(
+$x )' is.
f();
}
Upd.: My comment seems not to be truth. See further discussion - Re^6: printing unitialized value of the 'do BLOCK' proceeded by ikegami. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
|
|
|