Frew Schmidt
Frew Schmidt

Reputation: 9544

Is there an andand for Perl?

I'd rather do this:

say $shop->ShopperDueDate->andand->day_name();

vs. this:

say $shop->ShopperDueDate->day_name() if $shop->ShopperDueDate;

Any ideas?

(This idea is inspired by the Ruby andand extension.)

(Actually it is inspired by the Groovy language, but most people don't know that ;-)

update: I think that both maybe() and eval {} are good solutions. This isn't ruby so I can't expect to read all of the methods/functions from left to right anyway, so maybe could certainly be a help. And then of course eval really is the perl way to do it.

Upvotes: 4

Views: 385

Answers (4)

geocar
geocar

Reputation: 9305

I assume your second example should better be:

say $shop->ShopperDueDate ? $shop->ShopperDueDate->day_name() : undef;

instead of what it actually says.

Anyway, you can't do it with exactly that syntax without a source filter because undef isn't an object, but an unary operator, so it can't have any methods.

Instead, consider this:

package noop; 
our $maybe = bless [], 'noop';
sub AUTOLOAD { undef };

This $noop::maybe is an object; all of its methods return undef though. Elsewhere, you'll have a regular function like this:

sub maybe { $_[0] || $noop::maybe; }

Then you can write this:

say maybe($shop->ShopperDueDate)->day_name()

This works because "maybe" returns returns its argument if true, otherwise it returns our $noop::maybe object which has methods which always return undef.

EDIT: Correction! the ->andand-> syntax can be had without a source filter, by using an XS that mucks about with Perl's internals. Leon Timmermans made an implementation that takes this route. It replaces the undef() function globally, so it's likely to be a lot slower than my method.

Upvotes: 9

Leon Timmermans
Leon Timmermans

Reputation: 30225

I think I've just written it. I've just uploaded it to CPAN, you can find it here.

Upvotes: 4

ysth
ysth

Reputation: 98388

Something like (untested):

use Object::Generic::False;
sub UNIVERSAL::andand { $_[0] || Object::Generic::False->false }

UNIVERSAL is automatically a subclass of all other classes, so this provides andand for all objects. (Obviously creating methods in UNIVERSAL has the potential for conflicts or surprising action at a distance, so it shouldn't be used carelessly.) If the object upon which the andand method is called is true, return it; otherwise return a generic false object which returns itself for any method call used upon it.

Upvotes: 0

pjf
pjf

Reputation: 6009

You can use Perl's eval statement to catch exceptions, including those from trying to call methods on an undefined argument:

eval {
    say $shop->ShopperDueDate->day_name();
};

Since eval returns the last statement evaluated, or undef on failure, you can record the day name in a variable like so:

my $day_name = eval { $shop->ShopperDueDate->day_name(); };

If you actually wish to inspect the exception, you can look in the special variable $@. This will usually be a simple string for Perl's built-in exceptions, but may be a full exception object if the exception originates from autodie or other code that uses object exceptions.

eval {
    say $shop->ShopperDueDate->day_name();
};

if ($@) {
    say "The error was: $@";
}

It's also possible to string together a sequence of commands using an eval block. The following will only check to see if it's a weekend provided that we haven't had any exceptions thrown when looking up $day_name.

eval {
    my $day_name = $shop->ShopperDueDate->day_name();

    if ($day_name ~~ [ 'Saturday', 'Sunday' ] ) {
        say "I like weekends";
    }
};

You can think of eval as being the same as try from other languages; indeed, if you're using the Error module from the CPAN then you can even spell it try. It's also worth noting that the block form of eval (which I've been demonstrating above) doesn't come with performance penalties, and is compiled along with the rest of your code. The string form of eval (which I have not shown) is a different beast entirely, and should be used sparingly, if at all.

eval is technically considered to be a statement in Perl, and hence is one of the few places where you'll see a semi-colon at the end of a block. It's easy to forget these if you don't use eval regularly.

Paul

Upvotes: 9

Related Questions