When calling a function with named parameters, I will often find myself in a situation where I only really want to specify certain parameters if they are needed. This is particularly true when just the presence of a given parameter (regardless of its value) will have some consequence that I might want to avoid.

As with most things in Perl 5, there is more than one way to do this. But I will look particularly at two.

The bang-bang repetition

In the codebase at $work , I often see the following idiom being used:

some_function ( foo => 42 , ( bar => $bar ) x !! $bar , );

The first time I saw this it took a little to parse. Let’s break it down:

The ( bar => $bar ) is a list with two elements, which in this context will likely mean a key-value pair. The x after that is the list repetition operator, which

returns a list consisting of the left operand list repeated the number of times specified by the right operand.

The “left operand” in this case is the key-value pair we mentioned above. But … what about the right operand? That’s the !!$bar . The first bit is a “bang-bang”, which is a way to coerce a value into a boolean (the first bang negates it, and the second one negates it again).

In this case, since Perl 5 does not have “true booleans”, it will turn a true value into a 1 and a false value ( 0 , the empty string, or an undefined value) into the empty string (which numifies to 0 ).

The net effect is that ( bar => $bar ) will be appear once if $bar is true, and zero times if $bar is false.

Incidentally, a reader in Reddit mentioned that this operator is also mentioned in the “secret operators” page, where it’s called “enterprise”, as in the ship from Star Trek.

The ternary operator

An alternative way to achieve the same result is using a ternary operator. This looks like the following:

some_function ( foo => 42 , $bar ? ( bar => $bar ) : (), );

The ? ... : in this case is the ternary (or “conditional”) operator:

If the argument before the ? is true, the argument before the : is returned, otherwise the argument after the : is returned.

In this case, the argument before the : is the same key-value pair we’ve been discussing. And the one afterwards? That () is simply an empty list.

This means that, if $bar is true, it will return the key-value pair, and if it is not, it will return the empty list (and because the lists flatten out, it will have no effect).

Some differences

It might seem clear from my explanations of both of these, but I have a clear preference for the second version. I find it simpler, less magic, and in general easier to read and reason about.

But until now, that was basically it. My preference was a stylistic one, and I had no clear reason to prefer one over the other than the fact that … I preferred it.

Until now.

It turns out there is one fundamental difference between the two. And not just a difference, but more like the first way has a problem: it will evaluate the list (the key-value pair) even if it will be discarded.

This might not seem immediately problematic, but it turns out that, in some cases, evaluating the list might not be entirely safe.

Take the following example

my $object = maybe_an_object (); some_function ( foo => 42 , ( bar => $object -> bar ) x !! $object , # Might die );

In this case, the “value” in the key-value pair is the result of calling the bar method on $object . But like maybe_an_object() suggests, we don’t know whether $object will actually have an object or not (it could be defined and still be something that we cannot call methods on). In that case, calling a method on it will throw an exception (“die”).

And because we evaluate the list (meaning that we make that method call) even when the $object is false, this is not safe.

Compare this with the alternative:

my $object = maybe_an_object (); some_function ( foo => 42 , $object ? ( bar => $object -> bar ) : (), # Safe ... ish );

In this case, the key-value pair is only evaluated when $object is true.

Bonus track (because I can’t count)

Since this is Perl 5, there are of course other ways. Of these, I am particularly fond of a CPAN module by Toby Inkster called PerlX::Maybe, and if you are not opposed to bringing an extra dependency for this, I’d recommend considering it.

Using it looks like this:

use PerlX:: Maybe ; # Imports 'maybe' some_function ( foo => 42 , maybe bar => $bar , baz => 24 , );

As far as interfaces go, you can’t beat this. And it does exactly what you think it should: if $bar is defined (not true), it will include the bar => $bar bit into the outer list. Otherwise, it will clobber it. And any other arguments that come afterwards (like the baz => 24 ) get returned anyway, so you can embed it into a longer list.

Then again, that will also evaluate the pair before checking their values. So if you have a pair which might die, you’ll have to use one of the other methods it provides, like maybe provide_deref :

use PerlX:: Maybe ' provided_deref '; my $object = maybe_object (); some_function ( foo => 42 , provided_deref $object , sub { bar => $object -> bar }, baz => 24 , );

but at that point, it’s not clear to me that it’s actually an improvement.

Conclusion

If there’s one thing that distinguishes Perl 5 from other languages is the freedom it gives you to write things how you think they make most sense. To some, this feels paralising: there are too many ways, and there’s no way to decide which way is the right way.

I find that it’s often easier to embrace the idea that there is no single right way, and that what is right will depend on what you want to do, where you’re doing it, etc. For me, the idea that the language provides me with tools and lets me decide which one to use is liberating.

In this case, I like the simplicity of the ternary operator approach, and I think it’s what I will use most often. Specially since it does not require any external dependencies.

PerlX::Maybe is the kind of thing I think I would only use if it’s available (in fact if it is, I think I’d find it hard to avoid using it). But I can’t imagine it would make sense bringing it in just for this.

As for the alternative with the bang-bang, now that I know the caveat it has about unconditionally evaluating the list, I think I have reasons to actively discourage it when I see it.