3

I am defining a method with an optional argument. Typically I would use something like this:

sub foo {
    ($self, $optarg) = @_;
    $optarg ||= 1;
}

I would like the optional argument to be treated as boolean, with the default being true. This obviously won't work with the above, since $optarg will evaluate to false if the argument is not passed in.

Is there a way to differentiate the absence of an argument being passed and a value that evaluates to false?

jordanm
  • 33,009
  • 7
  • 61
  • 76
  • See [this related question](http://stackoverflow.com/questions/8124138/how-to-pass-optional-parameters-to-a-perl-function) for an approach using prototypes (which should usually be avoided). Also [related](http://stackoverflow.com/questions/3543569/default-argument-values-in-subroutines). Possibly useful [here](http://www.perlmonks.org/?node_id=161091). – Dennis Williamson Jan 11 '13 at 00:45

5 Answers5

10

If you have Perl version 5.10 or later then you can replace $optarg ||= 1 with $optarg //= 1, which will modify $optarg only if it is undef.

Alternatively, if you want to allow undef to be passed as an explicit false value, you can say $optarg = 1 if @_ < 2.

Borodin
  • 126,100
  • 9
  • 70
  • 144
4
my ( $self, $optarg ) = ( @_, 1 );

If it's "not passed in" then it won't be in the list and you'll get the 1. Otherwise, assuming that the user knows how the method works, they shouldn't pass in anything that can be taken as false as a second argument, unless they mean it to be taken as a false value. (Of course, I'm not saying that knowledgeable users would never pass in a false value for a boolean.)

Axeman
  • 29,660
  • 2
  • 47
  • 102
  • I don't understand what you're saying about not passing in a false as the second argument. – Dennis Williamson Jan 11 '13 at 00:05
  • I don't understand what he is saying there either, but the code appears to work. – jordanm Jan 11 '13 at 00:06
  • 1
    He's saying that if the user passes in two arguments (so `@_` has two elements), then the `1` won't get assigned. If `@_` has one element, then `1` gets assigned to `$optarg`. And if `@_` has no elements, the caller gets what he deserves. – mob Jan 11 '13 at 00:14
  • @mob: That's what his code says. I disagree that that's what his prose says. If jordanm's code is well written, then it will handle any possible cases properly. – Dennis Williamson Jan 11 '13 at 00:36
4

This scales well to multiple optional arguments:

my $self = shift;
my $optarg = @_ ? shift : 1;

So does this, but it handles multiple required arguments better. Unfortunately, it violates the least-redundancy-possible principle.

my ($self, $optarg) = @_;
$optarg = 1 if @_ < 2;
ikegami
  • 367,544
  • 15
  • 269
  • 518
2

You have gotten some good responses, so I'm just going to add one more possible option. Method::Signatures gives a nice syntax for methods with signatures including default values, handled in the way you expect.

#!/usr/bin/env perl

package MyClass;

use Moo;
use Method::Signatures;

method foo ( $optarg = 1 ) {
  print "$optarg\n";
}

package main;

my $obj = MyClass->new;
$obj->foo;
$obj->foo(0);

Note that I use Moo but its not needed for M::S. Note also that Moo enables strict and that this script is strict safe, even though it may not look it.

Joel Berger
  • 20,180
  • 5
  • 49
  • 104
1
my $optarg;
$optarg = 1 if not defined $optarg;

But do note @jordanm's comment, below

hd1
  • 33,938
  • 5
  • 80
  • 91
  • This works in all cases except for `foo(undef)`, which the user could expect to evaluate to false. – jordanm Jan 11 '13 at 00:02
  • 2
    [`my $x = ... if ...` is an unreliable construct](http://stackoverflow.com/q/14197909/168657). Say `my $optarg; $optarg=1 if ...` instead. – mob Jan 11 '13 at 00:11