Charles DeRykus said:
On 5/25/2013 5:59 AM, Dr.Ruud wrote:
[...]
And the return-value of the eval{} must be checked, after forcing it to
1 for success. Etc.
And, even with that, it should be mentioned Try::Tiny is a better
solution.
In the sense that it has three times as much 'voodoo coding' but
(hark!) nobody needs to look at the mess. Apart from that, these
opinion statements regarding 'proper error handling' would be more
convincing if they were expressed as opinion statements and came with
a reason eg "I'm generally of the opinion that exceptions shouldn't be
used for error handling because ...".
I'm using eval and die extensively, both for aborting some sequence of
processing steps from nested subroutines and for actual exception
handling and this works very nicely. Some remarks about Try::Tiny:
,----
| finally (&;$)
|
| [...]
|
| This allows you to locate cleanup code which cannot be
| done via local() e.g. closing a file handle.
`----
This would be more aptly described as 'This construct is necessary to
work around deficiencies of a sixty year old concept for "automatic
memory management" which is limited to manageing memory while all
other resources which need to be allocated and freed have to be
managed with explicitly written code. That's not a problem for Perl 5
which uses a more modern approach for automatic resource management
with support for stack unwinding, deterministic finalization and
automatic management of file handles *BUT* it will become a problem
with Perl 6, should that ever evolve beyond being an abandoned Haskell
demo."
,----
| There are a number of issues with eval.
`----
,----
| Clobbering $@
|
| When you run an eval block and it succeeds, $@ will be cleared,
| potentially clobbering an error that is currently being caught.
`----
That's an inherent limitation of the idea to use 'a global variable'
as 'the exception location' and part of the documented functionlity of
eval. The solution is that someone who wants to use $@ for exception
handling has to save the value in some other location before starting
any more complicated unrelated computation.
,----
| Localizing $@ silently masks errors
|
| Inside an eval block, die behaves sort of like:
|
| sub die {
| $@ = $_[0];
| return_undef_from_eval();
| }
|
| This means that if you were polite and localized $@ you can't die in
| that scope, or your error will be discarded (printing "Something's
| wrong" instead).
`----
'Localizing $@# does not 'silently mask errors', it localizes $@, that
is, it creates a lexically scoped binding for a global
variable. Because of this, changes to $@ while this binding is in
scope will not affect 'the outside world' anyhow. Since $@ is used for
exception propagation, this means that code which localizes $@ without
dealing with this possibility 'might silently mask an error' aka 'is
broken'.
,----
| $@ might not be a true value
|
| This code is wrong:
|
| if ( $@ ) {
| ...
| }
|
| because due to the previous caveats it
| may have been unset.
`----
This example is incomplete: The code supposed to set $@ is
missing. Also, there were no general 'previous caveats': The first
situation roughly amounts to the following:
eval {
...;
...;
}
eval {
...;
...;
}
if ($@) {
}
plus the expectation that the value of $@ would reflect something
which happened during the first eval which it doesn't: This is a
coding error and needs to be avoided. Localizing $@ without dealing
with its 'special magic' is also a coding error.
,----
| The classic failure mode is:
|
| [...]
|
| In this case since Object:
ESTROY is not localizing $@ but still uses
| eval, it will set $@ to "".
`----
That's the sole valid concern so far: Object destructors are executed
automatically during stack unwinding. Because of this, it is prudent
to write them such that they don't modify any kind of 'state
information' visible to unrelated parts of the program. That's the
downside of any 'convenience mechanism' which might lead to the
execution of subroutines in places where this isn't obvious when
looking at the code causing these invocation. 'Operator overloading'
suffers from the same problem. Not related to eval/ $@ in any
particular way.
NB: This text is an opinion statement itself and there might well be
valid counterarguments for anything contained in it.