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

Re: Question/suggestion on perlfunc.pod example

Thread Previous | Thread Next
From:
Glenn Golden
Date:
May 12, 2015 22:30
Subject:
Re: Question/suggestion on perlfunc.pod example
Message ID:
20150512223031.GE581@huh.zplane.com
Aristotle Pagaltzis <pagaltzis@gmx.de> [2015-05-12 15:07:36 +0200]:
> * Glenn Golden <gdg@zplane.com> [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.
> 

Makes sense.

>
> 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).
> 

I appreciate your point, but see below for a possible alternative.

>
> 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.
> 

I like it too. But countering somewhat on the point about the return value
(and trying to finesse my ignorance here for a worthwhile cause): How about
including the return value test in the example simply as a comment-only
clause, just for pedagogical purposes, since it is explicitly discussed just
prior in the text.  So perhaps just augmenting your version like this or so:

    {
        local ($!, $@);
        my $result = do $file;
        if ($@)
            { warn "Couldn't run $file: $@" }
        elsif ($! and not defined $result)
            { warn "Couldn't load $file: $!" }
	else
            { # Here if final statement of $file evaluates to 'false' }
    }


The reason I like this is simply because then the example includes all the
eventualities, which is just what I (as a non-expert Joe User) was looking
for when reading up on 'do' and came across this. I understand your point
that the test doesn't have much practical value outside of a particular usage
context such as reading a config file, but it's nevertheless useful to folks
like myself just to see it explicitly called out, as an illustration of the
semantics of the return value.

>
> 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,
> however.
> 
> I think the best we can do is note these issues in the documentation.
>

OK, I'll try to include in the suggested patch some language that briefly
summarizes those points. 

> 
> > 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`.
> 

Ah yes, of course (and obvious too, now looking at it).


> 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.
> 

That's exactly what happened. :) Since I didn't really have a clear idea of
what I was doing (monkeys-with-xterms) I just tried it and then immediately
got rid of it when it didn't behave as expected with a non-existent file.

Let me know what you think of the above re-modified example, and then I'll
try making the appropriate changes to the surrounding text as well, and
post a suggested patch containing it all.

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About