Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

Re^3: RFC: Defined-Or for before Perl 5.10

by afoken (Abbot)
on Nov 02, 2010 at 17:06 UTC ( #869047=note: print w/replies, xml ) Need Help??

in reply to Re^2: RFC: Defined-Or for before Perl 5.10
in thread RFC: Defined-Or for before Perl 5.10

Nice. But ... ;-)

The next thing your users will ask for is defined_or_warn(), and if only for symmetry. It would contain nearly the same code as the final defined_or_die(), essentially a no-brainer.

Then, many people (me included) prefer to use Carp instead of die() and warn(), and for them, having defined_or_carp(), defined_or_croak(), defined_or_confess(), and defined_or_cluck() would be natural. Other people would get mad if you always used Carp inside your module, so loading Carp should perhaps happen at runtime, or only when one or more of the four Carp-Class functions are exported.

defined_or_call() is the generic solution for all of these cases, so you will probably end wrapping that function for all of the six warn()ing and die()ing functions.

The only problem I see with these shortcuts is that they perhaps won't work reliably because the string argument may be interpolated too early:

#!/usr/bin/perl use strict; use warnings; use 5.010; # only for // and say sub failing_func { $!=shift; return undef; } sub defined_or_warn { my ($value,$error)=@_; return $value if defined $value; warn $error; } sub defined_or_warn_reverse { my ($error,$value)=@_; return $value if defined $value; warn $error; } $!=1; say "error 1 => $!"; $!=2; say "error 2 => $!"; $!=3; say "error 3 => $!"; my $v=failing_func(1) // warn "oops: $!"; # should give error 1 $v=defined_or_warn(failing_func(2),"oops: $!"); # should give error 2 $v=defined_or_warn_reverse("oops: $!",failing_func(3)); # should give +error 3

... gives ...

error 1 => Operation not permitted error 2 => No such file or directory error 3 => No such process oops: Operation not permitted at line 31. oops: No such file or directory at line 17. oops: No such file or directory at line 24.

... on 5.10.0 (Linux and Windows Strawberry). So, no problem here as long as the error string is on the right hand side of the maybe-undefined value.

But who guarantees that function arguments are always evaluated from left to right? Maybe that fact is documented somewhere, I'm too lazy to search right now, and after all, it's your idea. ;-)

Another quite obvious problem is the wrong line number in the second and third error message. Your module would have to compensate that. You will probably end re-implementing or using parts of Carp.

CGI::Carp could also be a little problem. It uses Carp internally, but it also redefines die() and warn(), and it exports its own, modified versions of confess(), croak(), carp(), and cluck(). I think a possible workaround would be to detect a loaded CGI::Carp at runtime (if exists $INC{'CGI/'}?) and call its functions instead of the original ones.

Another nice shortcut could replace //=, something like defined_or_assign(\$var,$value), or even defined_or_assign($var,$value) when the wrapper has a prototype of (\$$). For boolean shortcuts, you would again need a callback function (defined_or_assign($var,expensive_or_killing_function(...)) vs. defined_or_assign_return_value_of($var,sub { expensive_or_killing_function(...) })).

And when you really implement defined_or_assign_return_value_of(), think about a shorter name for it. The other function names are also awfully long, perhaps you could export shorter aliases for them (def_or(), def_or_die(), def_or_warn(), ..., def_or_set(), def_or_set_rv()).


Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Replies are listed 'Best First'.
Re^4: RFC: Defined-Or for before Perl 5.10
by molecules (Monk) on Nov 10, 2010 at 20:13 UTC

    Thank you very much. Good call on not providing defined_or_die etc. That would be a lot of maintenance headache.

    I think I will start out with just def_or and dor_call for now. Once I get copyright issues worked out at work, I'll upload to CPAN.


      def_or is a good name, short and quite obvious. Many computer languages use "def" as a shortcut for "define" or "defined". So, no surprises here.

      dor_call, on the other hand, is not so obvious, you have cut away too much. What the heck is "dor"? A mis-spelled door? And what would be a door-call? "Don't make me think!" def_or_call is longer, but also clearer.

      So, now that I have def_or_call(), my code will very soon look very ugly and my fingers will bleed from the many keystrokes that I need:

      # Perl >= 5.10 my $pid=fork() // die "Can't fork: $!"; # Perl < 5.9 manually my $pid=fork(); defined($pid) or die "Can't fork: $!"; # 123456789012345 # 15 extra characters # Perl < 5.9 + your module my $pid=def_or_call(fork(),sub { die "Can't fork: $!" }); # 123456789012345678 # 18 extra characters -- oops! # And you should never forget to wrap die in a sub ... # Perl < 5.9 + your module with defined_or_die # (remember to check if arguments are ALWAYS evaluated left-to-right!) # (or find a better solution ...) my $pid=def_or_die(fork(),"Can't fork: $!"); # 12345 # just 5 extra characters - 10 less than manually # and no wrapper needed

      So, I think you should really provide def(ined)_or_die and def(ined)_or_warn. Sure, it is more work than the other functions. You should really look into the Carp sources to see how they cope with the problems your module will also have. Like I said, I'm very sure that you can use Carp for the line number problem. You could load it at runtime, inside def_or_die()/def_or_warn(). Something like this:

      sub def_or_die { my ($v,$msg)=@_; return def_or_call($v,sub { require Carp; die Carp::some_magic_helper_function($msg); }); }

      And I think you should also provide their Carp equivalents, for the same reasons. You could export them on demand only, and load Carp at runtime. Something like this:

      sub def_or_carp { my ($v,$msg)=@_; return def_or_call($v,sub { require Carp; Carp::please_ignore_these_wrappers(); Carp::carp($msg); }); }

      Support for CGI::Carp would look about like this:

      sub def_or_carp { my ($v,$msg)=@_; return def_or_call($v,sub { if (exists $INC{'CGI/'}) { # CGI::Carp loaded CGI::Carp::please_ignore_these_wrappers(); CGI::Carp::carp($msg); } else { require Carp; Carp::please_ignore_these_wrappers(); Carp::carp($msg); } }); }


      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        Fortunately we upgraded to Perl 5.14. I recommend the same to anyone considering backporting features. The only problems this has caused related to a few scripts (downloaded from CPAN in fact), that started with hard-code shebang lines such as


Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://869047]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (5)
As of 2018-05-28 01:58 GMT
Find Nodes?
    Voting Booth?