develooper Front page | perl.perl5.porters | Postings from May 2015

Re: Question/suggestion on perlfunc.pod example

Thread Previous | Thread Next
Aristotle Pagaltzis
May 12, 2015 13:07
Re: Question/suggestion on perlfunc.pod example
Message ID:
* Glenn Golden <> [2015-05-12 03:00]:
> OK. How about something straightforward like this then:
>   ==================================================================
>   if (not $r = do $file)
>   {
>       if ($@)
>           { warn "parse error or exception 'do'ing $file: $@"; }
>       elsif ($! && not defined $r)
>           { warn "couldn't 'do' $file: $!";                    }
>       else
>           { warn "invalid configuration returned from $file";  }
>   }
>   ==================================================================

I greatly dislike the switch to `if (not ...)` because it disemphasizes
the `do $file` as the main point of the code.

Also the last error message makes no sense whatsoever. In fact checking
the return value at all doesn’t make any sense outside of a specific use
case (such as configuration loading).

So how about two birds with one stone:

        local ($!, $@);
        my $result = do $file;
        if ($@)
            { warn "Couldn't run $file: $@" }
        elsif ($! and not defined $result)
            { warn "Couldn't load $file: $!" }

This passes cursory testing.

I have to say, I like this. It covers the mechanics of `do` fully but
narrowly, instead of leaving the reader to extract the relevant parts
from a contrived use case example.

It’s still buggy, though. If you do something that sets `$!` inside the
loaded file, and it returns undef, then the `$!` check will trigger and
the error message will lie about what happened. I can’t think of any way
to handle that; the loaded file just has to localise $! itself.

The same failure mode exists WRT $@ in perls before 5.14 – if you have
e.g. `eval { die };` inside the loaded the file, then the example code
will lie and say it threw an exception. Recent perls are not affected,

I think the best we can do is note these issues in the documentation.

> For reasons I don't fully understand, when "local($!, $@)" is added
> prior to "if ($@)" [i.e., mimicking what you had shown], and then
> running it with a non-existent filename, it winds up taking the 'else'
> clause rather than the expected 'elsif' clause with $! = "no such file
> or dir".

If “prior to `if ($@)`” means “after `do $file`” then that is entirely
to be expected. What `local` does is to clear those variables for the
duration of the current block scope and restore them afterwards, and if
you clear `$!` after the `do`, well then of course that branch can never
be taken. The point of putting the `local` there is to isolate the `do`
and its subsequent conditionals from previous values in those variables.
So it has to come *before* the `do`.

In fact if you put it after `do $file` then the surprise would be not
the failure of the `$!` check but rather that the `if ($@)` branch would
be taken correctly. The only explanation I can think of is that you only
tested the `$!` case with the misplaced `local` and gave up immediately
after that failed, never noticing that the `$@` case also fails.

Aristotle Pagaltzis // <>

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About