either the code doesn't do what the documentation says
It does what the docs says. The documentation just glosses over details that turn out to be relevant when magic vars are involved. (Some people think this kind of abstraction is a good thing.)
The fact that my and local are treated as directives by the docs hindered my understanding of them when I started using Perl. They truly are functions (named operators), and the "initializer" assignment is unrelated to them.
By the way, my clears its argument variables whether an "initializer" is present or not as well, but since magic is removed too, there are no side-effects.
I only wish I could see how that informs the simpler case of:
They're identical to Perl. Perl doesn't have a concept of initializers. It sees a local, and it sees an assignment. It evaluates both independently.
I cannot imagine a useful purpose for the fact that local $foo = 76 ; fleetingly sets $foo to undef, before setting it to the required 76.
There isn't. There's a useful purpose for local $foo to set $foo to undef. In your statement, local $foo is being used for its side-effects, but it's primary function still occurs.
What you ask for requires local to check the optree to see if it's evaluated as the LHS of an assignment. I normally abhore anything that peeks at the optree to determine behaviour. Such code is inherently fragile. It works most of the time, leading people to think it always works, leading to subtle bugs. That makes the situation worse.
But in this case, we're already in that situation. So maybe it is appropriate here.
[Using POSIX to block the signal during the local] turns out to be essential in order to use local to automatically restore the $SIG state on forced exit (die) from an eval.
Not so. I already provided code that doesn't use POSIX or local and restores the signal automatically on scope exit.
| [reply] [d/l] [select] |
It does what the docs says. The documentation just glosses over details that turn out to be relevant when magic vars are involved. (Some people think this kind of abstraction is a good thing.)
Certainly I think a good abstract model is a Good Thing. Inter alia it makes the documentation easier both to write and to understand. It should also guide the implementation. We can now discuss what is, or is not, a good abstract model, if you like... but I think we are straying from the point.
One can take the view that what the implementation does is what it should do; and that the documentation is only a rough and ready guide to that.
In this case, an implementation detail that didn't seem relevant, turns out to have a nasty side effect. Unfortunately, that side effect also invalidates other advice in the documentation, and (I believe) common practice. One can take the view that this is what happens when people depend on a description of the language, or (worse yet) some abtract model, instead of the actual implementation. Time to go and fix the documentation, the advice and the common practice... perhaps.
On the other hand, if only for the obvious utility, perhaps local should be more aware of its context (small 'c'), and be changed to not set a variable undef when there is an initializer -- in addition to not changing it before the initializer has been evaluated. If that is what you meant by:
But in this case, we're already in that situation. So maybe it is appropriate here.
then I am happy to leave it at that (and leave to one side the question of pin heads and angels).
However, I cannot resist a debating point when I see one... NB: the following adds nothing to the real issue, so disinterested readers may which to move on to something else now.
They're identical to Perl. Perl doesn't have a concept of initializers. It sees a local, and it sees an assignment. It evaluates both independently.
The implementation may have no concept of initializers, but the language description does. While there is no dissonance it's immaterial -- a distinction that makes no difference. When there is dissonance, however, we seem to disagree on how to resolve the problem.
I understand that local is an operator (a "Named Unary Operator"), which has a higher precedence than '='. It was a small surprise to me, therefore, that:
our $fred = 78 ;
local $fred = $fred ? "Still $fred" : 'undef' ;
sets $fred to 'Still 78'. After all: $fred = 78 ;
$fred = d($fred) + $fred ;
sub d { $_[0] = 10 ; return 1 ; } ;
sets $fred to 11 -- apparently because the term d($fred) has higher precedence than '+' (noting that $fred + d($fred) gives the same result).
This suggests to me that local is not being evaluated independently of the assignment. Either that, or there is something about the precedence of local and the order of execution which is not the same as the precedence and order of execution of a function call. While the documentation swears blind that Perl functions "can serve as terms in an expression", the behaviour of local with a assignement, is more akin to a declaration.
Of course, I know that it is foolish to assume such simple model, and that I should read the documentation carefully to check what local does in any given circumstance (avoiding the word context, you understand). There I discover that local sets its arguments undef in the absence of an initializer. The documentation is silent on how the old value is saved, when the old value is saved, or whether that process sets the variable undef or not. I see now that I was compounding my original errors, and that I should really consult the code, rather than the documentation !
Second, reaching back up this thread, I said, in the context of local $foo = 78 ; (or similar):
Were it not for the side effect of setting $SIG{ALRM} to undef, nobody would ever be able to tell that being set undef is apparently a step in the initialisation of the local.
To which you replied:
Not so. I rely on that feature all the time.
Which, I confess, surprised me, so I admitted:
I cannot imagine a useful purpose for the fact that local $foo = 76 ; fleetingly sets $foo to undef, before setting it to the required 76.
to which you now respond:
There isn't.
which is a bitter disappointment to me, because I was really looking forward to something wonderful :-(
Finally, your alternative way of automatically restoring a $SIG entry on scope exit is characteristically clever. Thank you. I will add Sub::ScopeFinalizer to my list of potentially useful stuff.
However, I had suggested that to continue (safely) to use local $SIG{..} = sub ..., in the way described in the documentation, requires the local to be wrapped... To which you replied:
Not so. I already provided code that doesn't use POSIX or local and restores the signal automatically on scope exit.
which leaves me wondering where the contradiction is :-(
| [reply] [d/l] [select] |
sub d :lvalue { print("$_[0]\n"); $_[1] }
d("LHS", 4 ) + d("RHS", 5); # LHS, RHS
d("LHS", $var) = d("RHS", 1); # RHS, LHS
This was specifically done to handle expressions of the form f($var) = g($var). That's why $x = $x + 1 works. That's why my $x = $x; works.
which leaves me wondering where the contradiction is
Sorry, I missed the bold portion in "This second turns out to be essential in order to use local to automatically restore the $SIG state on forced exit (die) from an eval."
| [reply] [d/l] [select] |