http://www.perlmonks.org?node_id=868361

molecules has asked for the wisdom of the Perl Monks concerning the following question:

Dear Perlmonks,

I wanted a defined-or function that works on Perls before 5.10. I tried to find something like it but "defined" and "or" are such common words, I finally gave up searching.

I submitted this RFC to modules@perl.org but did not get any responses.

To illustrate its use, here are some results copied from running my test file.

ok 1 - 0 = defined_or( undef, 0);
ok 2 - 0 = defined_or( 0, undef);
ok 3 - 0 = defined_or( undef, defined_or( undef, 0));
ok 4 - 1 = defined_or( undef, 1);
ok 5 - 1 = defined_or( 1, undef);
ok 6 - 1 = defined_or( undef, defined_or( undef, 1));
ok 7 - undef = defined_or( undef, undef);
ok 8 - undef = defined_or( undef, undef);
ok 9 - undef = defined_or( undef, defined_or( undef, undef));
ok 10 - 1 = defined_or( 1, 0);
ok 11 - 0 = defined_or( 0, 1);
ok 12 - 1 = defined_or( 1, 1);
ok 13 - 1 = defined_or( 1, 1);

And the POD:

SYNOPSIS
        use DefinedOr qw/ defined_or /;

        my $foo = defined_or( shift(), 1); 
        my $bar = defined_or( $baz, defined_or( $boo, 1));

Description
    This is a simple implementation of defined/or (without the beautifully
    simple syntax) for those that are not using Perl 5.10 for some reason.

EXPORTED SUBROUTINES
  defined_or
    This subroutine takes two arguments. It returns the first argument if it
    is defined. Otherwise, it returns the second argument.

    Yes, this is actually a subroutine, not pretty syntax like //.

INCOMPATIBILITIES
    None reported.

BUGS
    None have been reported as of the current release. Please report any 
    bugs to bug-definedor@rt.cpan.org.

LIMITATIONS
    Currently runs fine on Perl 5.8.8. If someone wants to try it on older
    Perls, then delete the line that says use 5.008;. If you do, 
    please let me know what version of Perl you're running it on. 

AUTHOR
    Christopher Bottoms, "<molecules at cpan.org>"

ACKNOWLEDGEMENTS
    Thanks to all those who developed the true defined-or for Perl 6 and 
    Perl 5.10. This is a weak imitation.

LICENSE AND COPYRIGHT
    This program is free software; you can redistribute it and/or modify it
    under the terms of either: the GNU General Public License as published
    by the Free Software Foundation; or the Artistic License.

    See http://dev.perl.org/licenses/ for more information.

    Copyright 2010 Christopher Bottoms.

What do you think?

Thanks!

Replies are listed 'Best First'.
Re: RFC: Defined-Or for before Perl 5.10 (def(...))
by tye (Sage) on Oct 29, 2010 at 19:12 UTC

    See def -- Deal nicely with undefined values that I posted in 2000. But I'd actually do it differently:

    sub def { my $var= \$_[0]; my $val; while( @_ && ! defined $val ) { $val= shift @_; } $$var= $val if ! defined wantarray; return $val; } # For example: my $size= def( $ENV{MAXSIZE}, -s $swapFile, -1 ); # Or void context causes $size to be modified def( my $size, $ENV{MAXSIZE}, -1 );

    - tye        

Re: RFC: Defined-Or for before Perl 5.10
by JavaFan (Canon) on Oct 29, 2010 at 17:27 UTC
    H.Merijn Brand used to maintain patches against 5.8.x to enable // and //=. You may want to check his CPAN account to see whether they are still there.

    Note that if you want to go the subroutine way, you could also do:

    use List::Util qw[first]; my $val = first {defined} undef, 0;

      Thanks!

      The patches are huge, running 350-500K! (c.f. dor-5.8.8-34438.diff)

      He names the patch files starting with "dor". Would that be a better name than "defined_or"?

        He names the patch files starting with "dor". Would that be a better name than "defined_or"?
        I personally prefer "dor" to "defined_or". Actually, I quite like Larry's original "err" -- though the majority of P5P seemed to prefer "dor", based on some long P5P threads. To be "consistent" with Perl 6, you might even consider "orelse".

        Note that while the // "defined or" operator was added to Perl 5.10, the low precedence version of this operator was not. AFAICT, this low precedence operator morphed from err to dor to orelse; orelse is in Perl 6 as a similar, but not identical, low precedence version of //, but it's not in Perl 5 and I'm not aware of any plans to add it. See also this Larry response on perl6-language mailing list.

Re: RFC: Defined-Or for before Perl 5.10
by afoken (Chancellor) on Oct 30, 2010 at 08:25 UTC

    I see a little problem with using a function instead of an operator: Boolean shortcuts won't work, the function call will always evaluate both sides.

    my $pid=fork() // die("Can't fork: $!"); # die()s when fork() failed

    vs.

    my $pid=defined_or(fork(),die("Can't fork: $!")); # always die()s, twice when fork() was successfully evaluated before d +ie().

    Alexander

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

      Thanks! You've really got me thinking.

      I'm going to add another couple of subroutines:

      my $pid = defined_or_die( fork(), "Can't fork: $!");
      my $pid = defined_or_call( fork(), sub{ die "Can't fork: $!"});

      The "defined_or_call" subroutine will only evaluate the second argument if the first argument is undef.

        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 foo.pl line 31. oops: No such file or directory at foo.pl line 17. oops: No such file or directory at foo.pl 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/Carp.pm'}?) 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()).

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        sub _defined { if (defined($_[0])) { return $_[0]; } else { return $_[1]->(); } } sub _or(&) { return $_[0]; } my $pid = _defined fork(), _or { die "Can't fork: $!" };

        I'll let you pick better names.

        Update: Nevermind. If you try to pick useful names, you end up back with

        my $pid = defined_or fork(), sub { die "Can't fork: $!" };
Re: RFC: Defined-Or for before Perl 5.10
by Anonymous Monk on Dec 02, 2010 at 01:15 UTC
    On a slightly unrelated topic, has there been zero interest in low precedence version of the "xor" operator? Just wondering...