Go Back   Rhinocerus > Newsgroup > Newsgroup comp.lang.perl.misc

Reply
 
Thread Tools Display Modes
  #31 (permalink)  
Old 03-16-2012, 08:37 PM
Rainer Weikusat
Guest
 
Posts: n/a
Default Re: $var = do { ... }?

tmcd@panix.com (Tim McDaniel) writes:
> In article <871uosoc6n.fsf@sapphire.mobileactivedefense.com >,
> Rainer Weikusat <rweikusat@mssgmbh.com> wrote:
>>Ben Morrow <ben@morrow.me.uk> writes:
>>> Quoth tmcd@panix.com:

>>
>>[...]
>>
>>>> my $result = do {
>>>> if ($i % 2 == 0) { 'even' }
>>>> elsif ($i % 3 == 0) { 'divisible by 3' }
>>>> elsif ($i % 5 == 0) { 'divisible by 5' }
>>>> else { 'just wrong' }
>>>> };
>>>>
>>>> Is there a clever way in Perl 5 to metaphorically return early with a
>>>> value?

>>
>>[...]
>>
>>> The other thing that works, and it is in fact documented though I had no
>>> idea until I just looked, is to return from an eval {}:
>>>
>>> my $result = eval {
>>> $_ % 2 == 0 and return "even";
>>> $_ % 3 == 0 and return "divisible by three";
>>> return "just wrong";
>>> };
>>>
>>> I'm not sure it's got much to recommend it over
>>>
>>> my $result = sub {
>>> $_ % 2 == 0 and return "even";
>>> return "odd";
>>> }->();
>>>
>>> though,

>>
>>The first is using a language construct according to its intended
>>purpose. The second is abusing a language construct in order to
>>emulate the first 'somehow'. That alone should be sufficient to avoid
>>it. In addition to that, it needs more test because the mock
>>subroutine created for this purpose also needs to be invoked and -
>>depending on whether the compiler special-cases this so that people
>>can indulge their passion for the bizarre[*] - it is probably also
>>less efficient.

>
> I infer that "first" means the eval example and "second" means the sub
> example.
>
> The notion of "abusing a language construct" in Perl is already a
> problematic notion. Perl is the polyperverted abuse bottom of
> languages.


The Perl code some people write is 'the polyperverted abuse bottom of
coding', this being enabled by the versatility of the language.

> The doc for eval says "This form is typically used to trap exceptions
> more efficiently than the first (see below), while also providing the
> benefit of checking the code within BLOCK at compile time." So using
> eval just to be able to return is not its "intended purpose".


this text continues with

In both forms, the value returned is the value of the last
expression evaluated inside the mini-program; a return
statement may be also used, just as with subroutines.

> In fact, the eval version is the one that needs the testing, not the
> sub. eval traps fatal terminations, and just about anything can
> die(). In this simple case, I think you'd need "use warnings FATAL =>
> 'numeric';". More realistic examples can die much easier. So you
> can't just replace do{...} with eval{...}; you'd have to add proper
> error checking and we've recently had a discussion about the edge
> cases to worry about.


Almost anything can be turned from a molehill into an insurmountable
moutain by sufficiently wild generalizations. None of this is an issue
for the actual example and there is a single 'edge case' code using
eval for exception handling needs to worry about (There are two more
if some module written by some confused guy is being used. But since
these 'edge cases' have been fixed in Perl 5.14.0, there's little
reason to do so).

> sub{...}, on the other hand, *is* a straightforward substitution.
> Also, for people like me who are used to Lisp and similar languages
> that make much more use of small functions created all over, it's a
> natural idiom.


If you want to make it a subroutine then do so. That would be a
sensible choice. Recreating an otherwise invariable anonymous
subroutine every time the code is executed in order to call it once
just to avoid structuring the code as hard as possible isn't.
Reply With Quote
Alt Today
Advertising
 
and become member of Rhinocerus
Standard Sponsored Links

  #32 (permalink)  
Old 03-16-2012, 09:56 PM
Ben Morrow
Guest
 
Posts: n/a
Default Re: $var = do { ... }?


Quoth tmcd@panix.com:
> In article <871uosoc6n.fsf@sapphire.mobileactivedefense.com >,
> Rainer Weikusat <rweikusat@mssgmbh.com> wrote:
> >Ben Morrow <ben@morrow.me.uk> writes:
> >> Quoth tmcd@panix.com:

> >
> >>> Is there a clever way in Perl 5 to metaphorically return early with a
> >>> value?

> >
> >> The other thing that works, and it is in fact documented though I had no
> >> idea until I just looked, is to return from an eval {}:
> >>
> >> my $result = eval {
> >> $_ % 2 == 0 and return "even";
> >> $_ % 3 == 0 and return "divisible by three";
> >> return "just wrong";
> >> };
> >>
> >> I'm not sure it's got much to recommend it over
> >>
> >> my $result = sub {
> >> $_ % 2 == 0 and return "even";
> >> return "odd";
> >> }->();
> >>
> >> though,

> >
> >The first is using a language construct according to its intended
> >purpose. The second is abusing a language construct in order to
> >emulate the first 'somehow'. That alone should be sufficient to avoid
> >it. In addition to that, it needs more test because the mock
> >subroutine created for this purpose also needs to be invoked and -
> >depending on whether the compiler special-cases this so that people
> >can indulge their passion for the bizarre[*] - it is probably also
> >less efficient.

>
> I infer that "first" means the eval example and "second" means the sub
> example.


I don't know what Rainer meant, but I would have said the reverse: using
eval {} as an anon sub rather than a try is not using it for its
intended purpose...

> The doc for eval says "This form is typically used to trap exceptions
> more efficiently than the first (see below), while also providing the
> benefit of checking the code within BLOCK at compile time." So using
> eval just to be able to return is not its "intended purpose".


....and I see you agree .

> In fact, the eval version is the one that needs the testing, not the
> sub. eval traps fatal terminations, and just about anything can
> die(). In this simple case, I think you'd need "use warnings FATAL =>
> 'numeric';".


Why? eval {} can warn perfectly well, if it wants to: in fact, by
converting warnings to errors you would end up hiding warnings that
would otherwise be printed. Even if you went to the trouble of catching
and printing them, Perl (5) doesn't have resumable exceptions, so
there'd be no way to get back the normal warning behaviour.

> More realistic examples can die much easier. So you
> can't just replace do{...} with eval{...}; you'd have to add proper
> error checking and we've recently had a discussion about the edge
> cases to worry about.


But only if you expect something to die, and only if it's not acceptable
for the eval to (silently) return an empty list in that case. If you
remember, the most important thing I said was 'if you use raw eval{},
check its return value rather than relying on $@', which is exactly what
you're doing here.

> sub{...}, on the other hand, *is* a straightforward substitution.
> Also, for people like me who are used to Lisp and similar languages
> that make much more use of small functions created all over, it's a
> natural idiom. Also, it has an argument list, so some complexity
> could be encapsulated there, allowing the body of the text to refer to
> $_[2] or assigning the argument to a variable as one likes.


There'd be little point, since it's a lexical closure. That is, you can
simply refer to outside variables without needing to pass them in.

> I don't know how to test efficiency. I've seen a few uses of some
> module to run a construct a number of times and report on the time,
> but I apparently wasn't using the correct search terms at CPAN.


Benchmark; it's in the core distribution.

I would expect that, if there's any measurable difference, eval is
slower than sub is slower than a plain block is slower than do, simply
because each is doing everything the next does and then a bit more.

Ben

Reply With Quote
  #33 (permalink)  
Old 03-16-2012, 10:54 PM
Rainer Weikusat
Guest
 
Posts: n/a
Default Re: $var = do { ... }?

Ben Morrow <ben@morrow.me.uk> writes:

[...]

>> More realistic examples can die much easier. So you
>> can't just replace do{...} with eval{...}; you'd have to add proper
>> error checking and we've recently had a discussion about the edge
>> cases to worry about.

>
> But only if you expect something to die, and only if it's not acceptable
> for the eval to (silently) return an empty list in that case. If you
> remember, the most important thing I said was 'if you use raw eval{},
> check its return value rather than relying on $@',


In absence of a 'special return value to signal an error condition'
convention the code happens to use, the return value from an eval can
be anything, including 0 or undef. Consequently, this doesn't and
cannot ever work except if the code isn't using exceptions for error
reporting but special return values.

[...]

> There'd be little point, since it's a lexical closure. That is, you can
> simply refer to outside variables without needing to pass them in.
>
>> I don't know how to test efficiency. I've seen a few uses of some
>> module to run a construct a number of times and report on the time,
>> but I apparently wasn't using the correct search terms at CPAN.

>
> Benchmark; it's in the core distribution.
>
> I would expect that, if there's any measurable difference, eval is
> slower than sub is slower than a plain block is slower than do, simply
> because each is doing everything the next does and then a bit more.


As a someone named Daniel Bernstein once quipped "Don't
speculate. Profile". For the extremely simple-minded example below,

----------
use Benchmark;

timethese(-5,
{
eval => sub {
return eval { return 3; };
},

sub => sub {
return sub { return 3; }->();
}});
----------

creating an anonymous throwaway subroutine per invocation is about
half as fast as the eval. This might be different when avoiding this
grotty hack in favor of using a proper, named subroutine (you can
always name it Maeusedreck if you fear that this might render your
code a tad bit to accessible to others ...)




Reply With Quote
  #34 (permalink)  
Old 03-17-2012, 05:48 PM
Tim McDaniel
Guest
 
Posts: n/a
Default Re: $var = do { ... }?

Concerning
my $result = eval {
$_ % 2 == 0 and return "even";
$_ % 3 == 0 and return "divisible by three";
return "just wrong";
};

In article <41tc39-jv12.ln1@anubis.morrow.me.uk>,
Ben Morrow <ben@morrow.me.uk> wrote:
>Quoth tmcd@panix.com:
>> In fact, the eval version is the one that needs the testing, not
>> the sub. eval traps fatal terminations, and just about anything
>> can die(). In this simple case, I think you'd need
>> "use warnings FATAL => 'numeric';"

>
>Why? eval {} can warn perfectly well, if it wants to: in fact, by
>converting warnings to errors you would end up hiding warnings that
>would otherwise be printed.


I was too elliptical. Sorry. Expanding:

Just about anything can die, and eval will swallow the results. You
might think that this example is so simple that it couldn't possibly
die, and so eval is perfectly harmless in this case. However, you can
even make this die, though the only way I can think of is if to use

use warnings FATAL => 'numeric';

and a non-numeric value of $_.

>> can't just replace do{...} with eval{...}; you'd have to add proper
>> error checking and we've recently had a discussion about the edge
>> cases to worry about.

>
>But only if you expect something to die, and only if it's not
>acceptable for the eval to (silently) return an empty list in that
>case.


As I demonstrated, surprisingly simple things can die unexpectedly.
Personally, I don't consider something to be a solution if it works
only if nothing dies, but if it doesn't it SILENTLY SUPPRESSES AN
ERROR MESSAGE. When coding it, I'll overlook a way it can die, or
someone will not realize that it's fragile and will break the
assumptions. And if the eval block calls some other function, in
practice it'll be utterly doomed.

In sum: you can't just replace do{...} by eval{...}, and if you try
the result will be fragile, you'll need boilerplate code to propagate
errors, and it will suppress errors or otherwise break if you get it
wrong.

--
Tim McDaniel, tmcd@panix.com
Reply With Quote
  #35 (permalink)  
Old 03-17-2012, 09:53 PM
Ben Morrow
Guest
 
Posts: n/a
Default Re: $var = do { ... }?


Quoth tmcd@panix.com:
> Concerning
> my $result = eval {
> $_ % 2 == 0 and return "even";
> $_ % 3 == 0 and return "divisible by three";
> return "just wrong";
> };
>
> In article <41tc39-jv12.ln1@anubis.morrow.me.uk>,
> Ben Morrow <ben@morrow.me.uk> wrote:
> >Quoth tmcd@panix.com:
> >> In fact, the eval version is the one that needs the testing, not
> >> the sub. eval traps fatal terminations, and just about anything
> >> can die(). In this simple case, I think you'd need
> >> "use warnings FATAL => 'numeric';"

> >
> >Why? eval {} can warn perfectly well, if it wants to: in fact, by
> >converting warnings to errors you would end up hiding warnings that
> >would otherwise be printed.

>
> I was too elliptical. Sorry. Expanding:
>
> Just about anything can die, and eval will swallow the results. You
> might think that this example is so simple that it couldn't possibly
> die, and so eval is perfectly harmless in this case. However, you can
> even make this die, though the only way I can think of is if to use
>
> use warnings FATAL => 'numeric';
>
> and a non-numeric value of $_.


Oh, I see. Yes, I agree. There are other ways of making this die; the
most obvious would be an object in $_ with an overloaded % which dies.

Ben

Reply With Quote
  #36 (permalink)  
Old 03-18-2012, 02:40 PM
Rainer Weikusat
Guest
 
Posts: n/a
Default Re: $var = do { ... }?

tmcd@panix.com (Tim McDaniel) writes:
> Concerning
> my $result = eval {
> $_ % 2 == 0 and return "even";
> $_ % 3 == 0 and return "divisible by three";
> return "just wrong";
> };
>
> In article <41tc39-jv12.ln1@anubis.morrow.me.uk>,
> Ben Morrow <ben@morrow.me.uk> wrote:
>>Quoth tmcd@panix.com:
>>> In fact, the eval version is the one that needs the testing, not
>>> the sub. eval traps fatal terminations, and just about anything
>>> can die(). In this simple case, I think you'd need
>>> "use warnings FATAL => 'numeric';"

>>
>>Why? eval {} can warn perfectly well, if it wants to: in fact, by
>>converting warnings to errors you would end up hiding warnings that
>>would otherwise be printed.

>
> I was too elliptical. Sorry. Expanding:
>
> Just about anything can die, and eval will swallow the results.


Read: It is possible to attach hidden semantics to the code above in
various ways in order to turn eval into an unsuitable solution to the
original problem. Yes, of course. Name a solution of your choice. I
will find a way to break that by violating the invariant conditions it
relies on, probably even without resorting to using, say, dynamically
linked C code to wreak havoc onto some internal data structures.

> You might think that this example is so simple that it couldn't
> possibly die, and so eval is perfectly harmless in this case.


I think that eval is 'perfectly harmless' in the sense that it will
behave according to its documentation and that you're changing your
problem specification after the fact in order to fabricate reasons
against a possible solution you don't like for 'other reasons'.

[...]

>>But only if you expect something to die, and only if it's not
>>acceptable for the eval to (silently) return an empty list in that
>>case.

>
> As I demonstrated, surprisingly simple things can die unexpectedly.
> Personally, I don't consider something to be a solution if it works
> only if nothing dies, but if it doesn't it SILENTLY SUPPRESSES AN
> ERROR MESSAGE. When coding it, I'll overlook a way it can die, or
> someone will not realize that it's fragile and will break the
> assumptions. And if the eval block calls some other function, in
> practice it'll be utterly doomed.


In the kind of code you are probably envisioning, where this 'other
function' probably consists of 500 - 1500 lines of code whose exact
raison d'etre was lost with some guy who left the company five years
ago, where generations of successive maintenance programmers have
added patches to the patchwork of patches in order to work around this
or that problem, this is very likely true: As soon as the code calls
another function, only God knows what's going to happen (and if we
can't catch all these weird errors, where to can we hook our next
generation of workarounds).

But that's not a universal problem.
Reply With Quote
 
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off




All times are GMT. The time now is 02:26 AM.


Copyright ©2009

LinkBacks Enabled by vBSEO 3.3.0 RC2 © 2009, Crawlability, Inc.