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

Hi fellow monks,

We would like to write our own operand, an or that is based on if the first value is defined rather than if it is false or not. We were wondering if there is a way to create one's own operands. We see how to override one and know that or is not one we can override. We really just want to create our own.
We also know we can do:
defined( $foo = $bar->id ) or $self->error('blah blah')

What we want to be able to do is:
$foo = $bar->id definedor $self->error('blah blah')

Thanks for any advise.

Note: Thanks to jdporter for the advise but we are running 5.6.1 on our server and that does not work.

20060715 Unconsidered by Corion (Keep: 11, Edit: 15, Reap: 0): Was considered for : Retitle: create 'defined-or' (nav, search improvement)

Replies are listed 'Best First'.
Re: creating operands
by jdporter (Paladin) on Jul 13, 2006 at 16:58 UTC

    This operator is called "defined-or". In Perl, it written as //; there is also a low-priority form, err.

    I believe these were introduced in core Perl as of 5.9.2. They are also in Perl 6.

    So, for your example, you'd do

    $foo = $bar->id err $self->error('blah blah');
    We're building the house of the future together.

      H. Merjin Brand also distributes patches against the 5.8.x series for this. They can be downloaded from his CPAN directory.

      ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: creating operands
by ikegami (Patriarch) on Jul 13, 2006 at 17:49 UTC

    Sorry, even overload doesn't allow you to create new operators. A source filter is really your only option here.

    The only other solution that comes to mind is the following:

    sub definedor(&$) { defined($_[1]) ? $_[1] : $_[0]->() }

    However, the usage is uglier than your original code:

    definedor { $self->error('blah blah')) } $foo = $bar->id; # Ugly defined($foo = $bar->id) or $self->error('blah blah')); # Better
Re: creating operands
by Tanktalus (Canon) on Jul 13, 2006 at 17:41 UTC

    No. That was actually one of the issues that perl 6 is supposed to address: the ability to add new operators. All we have today are existing operators and functions.

    sub definedor { defined $_[0] ? $_[0] : $_[1] }
    I think that's as good as you're going to be able to do before perl 5.9.2.

    Of course, if you want a more generic one that can take multiple inputs, sort of emulating $a // $b // $c // ..., you could do:

    sub definedor { use List::Util qw(first); first { defined } @_ }
    and use it as definedor($a, $b, $c, ...) Of course, at this point, you almost may as well use the first function directly ;-)

      That doesn't work here. Your solution eliminates short-circuiting. Short-circuiting is crucial here since we only want $self->error() to execute when the LHS is undefined. Without short-circuiting, $self->error() will be called unconditionally.

      Short-circuiting is not even a bonus in the provided snippet. The OP uses or/definedor exclusively for its short-circuiting properties, since the return value of the or/definedor is discarded.

Re: creating operands
by Ieronim (Friar) on Jul 13, 2006 at 18:01 UTC
    As you cannot create definedor operand and don't want to wait for Perl 5.10, emulate it (differs from previos variants, as assigns $self->error to $foo, what you maybe (???) want to do):
    $foo = defined($bar->id) ? $bar->id : $self->error('blah blah');

      That's not equivalent. If you wanted to use the ternary operator,
      defined($foo = $bar->id) or $self->error('blah blah');
      would be equivalent to
      defined($foo = $bar->id) ? undef : $self->error('blah blah');

      $foo = defined($bar->id) || $self->error('blah blah');
      would be equivalent to
      $foo = defined($bar->id) ? $bar->id : $self->error('blah blah');
      but that's not relevant here.