Front page | perl.perl5.porters |
Postings from August 2012
Why smartmatch
Thread Next
From:
David Golden
Date:
August 24, 2012 06:54
Subject:
Why smartmatch
Message ID:
CAOeq1c8K-dzpTHMB1AQ89OQLo17QFSWEUycCbZ=f_LUGNY27Ow@mail.gmail.com
Just to step back, I thought I'd mention why I think smartmatch is a useful
feature given all the ambiguity we're discussing.
(1) It's a generic "test" operator
It's effectively $rhs->test($lhs). It makes it easier to work with a
collection of test "objects" without having to interrogate each one to know
how to resolve the test.
For some built-ins, the nature of 'test' is clear. For strings/numbers,
it's not, and the argument is really (a) do we pick one interpretation, (b)
do we try to guess based on $lhs or (c) do we refuse to smartmatch
(possibly with undef as a signal).
It works particularly nicely with junctions, where we can express things
like this:
if ( $lhs ~~ all( @list_of_tests ) ) { ... }
Instead of the much more verbose:
my $pass;
for my $t ( @list_of_tests ) {
if (ref $ eq 'CODE') {
$pass++ if $t->($lhs);
}
elsif (ref $t eq 'Regexp') {
$pass++ if $lhs =~ $t;
}
... # for all other possibly types of tests we can handle
}
if ( $pass == @list_of_tests ) {
...
}
In that long-hand form, if ref($t) is q{}, then we are forced to explicitly
choose whether to do == or eq or try to guess based on looks_like_number or
something else.
Just because smartmatch can't handle that case isn't too damning to me --
it still lets me avoid specifying all the things that *aren't* ambiguous.
[Frankly, I think we shot ourselves in the foot by calling it *smart*match
because we set expectations we can't live up to. If we'd called it the
"generic match" operator or something, we'd have been on safer ground.]
So, if we're not careful to avoid ambiguous scalars in our list of tests,
to be safe we need to clarify the desired fallback in the junction case:
if ( $lhs ~~ all( map { any($_, "$_") } @list_of_tests ) ) { ... }
That's still *much* easier than the longhand construct I wrote above. Is
it perfectly elegant? No, but this is the price we occasionally must pay
for dynamic typing.
(2) It allows less verbose "switch" constructs with "when"
Instead of a long chain like this:
if ( $lhs == 0 ) { ... }
elsif ( $lhs == 1 ) { ... }
elsif ( $lhs == 2 ) { ... }
...
else { ... }
We get to avoid writing "$lhs ==" in every clause by using the generic
match instead:
when (0) { ... }
when (1) { ... }
when (2) { ... }
default { ... }
That's less verbose, so easier to read.
The "problem" with "when" is that it inherits the generic match problem of
ambiguity.
when ($rhs) { ... } # what if $rhs is a plain scalar?
But we've proposed allowing an explicit test to serve the same
block-with-break function:
when {$lhs == $rhs} { ... }
That gives me an idea – maybe C<< when($rhs) { ... } >> should be
discouraged somehow.
The most severe way to do that would be to disallow *any* scalar variable
and only allow literals (including qr//) or "\&foo" references.
The gentlest way to do that would be to warn if a scalar $rhs in C<<
when($rhs) {...} >> is not a reference.
That would be orthogonal to the question of whether C<< $lhs ~~ $rhs >>
should warn if $rhs is ambiguous -- this would be specific to discouraging
when($rhs) as a construct so as to encourage explicit C<< when { $lhs ==
$rhs } { ... } >> or more generically C<< when ( any( $rhs, 0+$rhs ) ) {
... } >> instead.
David
--
*David Golden* <dagolden@cpan.org>
*Take back your inbox!* → http://www.bunchmail.com/
Twitter/IRC: @xdg
Thread Next
-
Why smartmatch
by David Golden