perlmeditation
Aristotle
<h2>I feel like a broken record</h2>
<p>Are you like me, and often find yourself wanting to include an element in a list conditionally (often depending on whether it is defined)?</p>
<p>F.ex., I may want to construct a URI which has a number of moving parts.</p>
<ul>
<li>In the simplest case, it may look like <tt>http://example.net/app/doc</tt>, where <tt>doc</tt> is a controller in the web app.</li>
<li>To link a specific item, the URI might be <tt>http://example.net/app/doc/42</tt></li>
<li>Some controllers are broken down further, giving things like <tt>http://example.net/app/doc/42/notes</tt></li>
<li>The app supports multiple sites, so I might also have something like <tt>http://example.net/app/joebob/doc</tt></li>
<li>To get the editing interface, you need to prefix the whole shebang accordingly, ie. for the <tt>joebob</tt> subsite: <tt>http://example.net/app/admin/joebob/doc</tt></li>
</ul>
<p>There are two dead obvious approaches to write this in Perl:</p>
<readmore>
<ol>
<li>
<p>The ugly [doc://push]-based version:</p>
<c>
my @part = ( 'http://example.net/app' );
push @part, 'admin' if $is_admin_link;
push @part, $subsite if defined $subsite;
push @part, $mode;
push @part, $id if defined $id;
push @part, $submode if defined $submode;
my $uri = join '/', @parts;
</c>
</li>
<li>
<p>The nicer approach with a ternary:</p>
<c>
# [updated: originally had `'admin' ? $is_admin_link : ()`]
my @part = (
'http://example.net/app',
( $is_admin_link ? 'admin' : () ),
( defined $subsite ? $subsite : () ),
$mode,
( defined $id ? $id : () ),
( defined $submode ? $submode : () ),
);
my $uri = join '/', @part;
</c>
</li>
</ol>
<p>But both approaches require you to repeat yourself unncessarily. In the [doc://push] case, you have to repeat the <tt>push @part</tt> bit, and in the ternary case, you have to supply <tt>: ()</tt> as an <i>else</i> clause for every case.</p>
<h2>Enter <tt>x!!</tt></h2>
<p>Obviously, this is a composite operator, consisting of <tt>x</tt> and two <tt>!</tt> negations.</p>
<p>The double negation is there to forcibly convert the right side to a boolean value with the same truthness as the original value:</p>
<c>
$a = undef;
print $a ? 'true' : 'false'; # prints 'false'
print $a; # prints '' and warns
$a = 0;
print $a ? 'true' : 'false'; # prints 'false'
print $a; # prints '0'
$a = 'abc';
print $a ? 'true' : 'false'; # prints 'true'
print $a; # prints 'abc'
# whereas
$a = undef;
print !!$a ? 'true' : 'false'; # prints 'false'
print !!$a; # prints '' without warning
$a = 0;
print !!$a ? 'true' : 'false'; # prints 'false'
print !!$a; # prints ''
$a = 'abc';
print !!$a ? 'true' : 'false'; # prints 'true'
print !!$a; # prints 1
</c>
<p>So the <tt>!!</tt> will force anything that shows up on the right side to be either <tt>1</tt> or <tt>!1</tt>.</p>
<p>The <tt>x</tt> is, of course, Perl’s repetition operator:</p>
<c>
@_ = ( 'a' ) x 4;
print join ':', @_; # prints 'a:a:a:a'
print scalar @_; # prints '4'
</c>
<p>Of interest to us are the cases where the number of repetitions is 1 or 0:</p>
<c>
@_ = ( 'a' ) x 1;
print join ':', @_; # prints 'a'
print scalar @_; # prints '1'
@_ = ( 'a' ) x 0;
print join ':', @_; # prints ''
print scalar @_; # prints '0'
</c>
<p>Since boolean values in Perl are either 1 or !1, that means <tt>( $something ) x $boolean</tt> will do exactly what we want. And we can force everything to be a boolean using the <tt>!!</tt> double negative. That is how we get <tt>x!!</tt>.</p>
<ins>
<p><strong>Update:</strong> <em>caveat codor</em> – as [ikegami] [id://564827|emphasises], the parens are <strong>required</strong>! <tt>( $something ) x $boolean</tt> does a very different thing from <tt>$something x $boolean</tt>. I knew this, but forgot to harp on it. (Yes, this difference is too subtle. In Perl 6, there will therefore be two different operators, <tt>x</tt> for strings and <tt>xx</tt> for lists.)</p>
</ins>
<p>Bottom line: the example I gave can be written like this:</p>
<c>
my @part = (
'http://example.net/app',
( 'admin' ) x!! $is_admin_link,
( $subsite ) x!! defined $subsite,
$mode,
( $id ) x!! defined $id,
( $submode ) x!! defined $submode,
);
my $uri = join '/', @parts;
</c>
<p>Neato! Not only is it nicer, but I also find that <tt>x!!</tt> has a beautifully evocative quality. <tt>:-)</tt></p>
<ins>
<p><strong>Update:</strong> but note that <tt>( $x ) x!! $cond</tt> differs from <tt>$cond ? $x : ()</tt> in shortcircuiting behaviour: the ternary will avoid evaluating <tt>$x</tt> if <tt>$cond</tt> is false, but <tt>x!!</tt> will always evaluate it.</p>
</ins>
<h2>Conventional notes</h2>
<p>You’ll notice that [doc://defined] always returns a boolean anyway, so the <tt>!!</tt> isn’t actually necessary in three of our cases, and might not be necessary in the first one either.</p>
<p>However, I find that one should use <tt>x!!</tt> anyway, for two reasons:</p>
<ol>
<li>It documents intent. Using <tt>x!!</tt> clearly signals that this is <em>not</em> just a repetition.</li>
<li>It protects you from errors. If the value of <tt>$is_admin_link</tt> is 2 for some reason, it is still <i>true</i> according to Perl, but in that case <tt>( 'admin' ) x $is_admin_link</tt> (<em>without</em> <tt>!!</tt>) will yield a very different result from <tt>( 'admin' ) x!! $is_admin_link</tt> (<em>with</em> <tt>!!</tt>).</li>
</ol>
</readmore>
<h2>Conclusion</h2>
<p>So there you have it. If you want to conditionally include a sub-list within a larger list, you can use the composite <tt>x!!</tt> operator express exactly that.</p>
<p align="right" class="pmsig pmsig-114691"><i>Makeshifts last the longest.</i></p>