Maybe a different approach
Last week I wrote about how Perl 5 gives us several ways to solve problems, and how we are free to choose among them based on what we think is important, clearer, more transparent (or opaque), etc. I wrote about how I liked the fact that in general Perl 5 trusts the developer to make that decision; and used a specific case to illustrate how I could choose between two (or more) alternatives.
Since then, I’ve thought a little more about it, and I think I’ve come up with another possible solution that I’d like to leave here for the record.
For some quick background: CPAN has PerlX::Maybe that offers a maybe
function that can be used to conditionally include elements in a list. This is
useful when key-value pairs in eg. a hash declaration should be conditionally
skipped. But because of the way maybe
is implemented, it’s not always safe to
use, and when used so it is, it becomes unwieldy.
So I propose a different maybe
:
sub maybe (&$@) {
my ( $block, $test ) = splice @_, 0, 2;
return @_ unless defined $test;
my @values = $block->();
if ( @values == 1 ) {
for (ref $values[0]) {
if ( /HASH/ ) { @values = %{ $values[0] } }
elsif ( /ARRAY/ ) { @values = @{ $values[0] } }
}
}
return ( @values, @_ );
};
This function takes a block of code and a value, and returns the result of calling the block only when the value is defined. Any additional parameters that are passed to the function after the value to check are returned at the end regardless.
The (&$@)
after the function name is a prototype, which roughly speaking
gives the Perl 5 interpreter a hint as to the context in which the different
parameters should be … interpreted. Specifically, it allows us to call this
function like this:
some_function(
foo => 42,
bar => 1,
maybe { baz => $obj->bar } $obj,
);
Thanks to the prototype, we don’t need the parentheses when calling maybe
,
and the { ... }
gets interpreted as a block of code we can conditionally (and
therefore safely) call. The $obj
after that (note the lack of commas!) is the
condition to check.
This call is functionally equivalent to
maybe( sub { baz => $obj->bar }, $obj )
which is itself largely equivalent to
sub { baz => $obj->bar }->() if defined $obj
The additional code in the declaration above makes it so that calling it like this
some_function(
foo => 42,
bar => 1,
maybe \&returns_arrayref => $check,
maybe \&returns_hashref => $check,
);
will also work, by conditionally calling the code references and dereferencing their return values into the return list. PerlX::Maybe has some additional features to de-reference blessed objects, but this seems to me like a bad idea, so I’m happy to skip that.
Needless to say, I have never used this in anger, so there may be bugs that
I’ve missed or situations that I haven’t considered. But as a statement of
what I’d like to be able to do with a maybe
function, I think it does the
job.
There may also be additional features that could be added, but if there’s one thing that I’ve learned from professionaly developing code and releasing some publicly, it’s that the more things are supported, the harder the maintenance.
Now that I’ve written this down, this feels like a surprisingly obvious thing to say.