Pretty soon after I started writing Perl in 1994, I noticed that it lacked a construct often found in other languages: the switch statement. Not to worry, though—you can achieve the same effect with a cascading series of if-elsif statements, right?
Well, no, you shouldn’t do that, especially if the chain is super-long. There’s even a perlcritic policy about it, which suggests that you use given and when instead.
But given and when (and the smartmatch operator they imply, ~~) are still experimental, with behavior subject to change. So, what’s a responsible developer to do?
The answer is to use the for statement as a topicalizer, which is a fancy way of saying it assigns its expression to $_. You can then use things that act on $_ to your heart’s content, like regular expressions. Here’s an example:
for ($foo) {
/^abc/ and do {
...
last;
};
/^def/ and do {
...
last;
};
# FALL THRU
...
}
This will cover a lot of cases (ha-ha, see what I did there? A lot of languages use a case statement… oh, never mind.) And if all you’re doing is exact string matching, there’s no need to bring in regexps. You can use a hash as a lookup table:
my %lookup = (
foo => sub { ... },
bar => sub { ... },
);
$lookup{$match}->();
EDIT: If every alternative is an assignment to the same variable, a ternary table is another possibility. This is a chained set of ternary conditional (? :) operators arranged for readability. I first heard about this technique from Damian Conway’s Perl Best Practices (2005).
# Name format # Salutation
my $salute = $name eq '' ? 'Dear Customer'
: $name =~ /(Mrs?[.]?\s+\S+)/ ? "Dear $1"
: $name =~ /(.*),\s+Ph[.]?D/ ? "Dear Dr. $1"
: "Dear $name"
;
Note that although this is just as inefficient as a cascaded-if/elsif, it’s clearer that it’s a single assignment. It’s also more compact and reads like a table with columns of matches and alternatives.
Any of these patterns are preferable to cascading if/elsifs. And if you want to check the development of given, when, and ~~, see this issue on GitHub. The last comment was eight years ago, though, so I wouldn’t hold my breath.


Comments
6 responses to “Switching up my switches in Perl”
I’ve found this to be no more complicated than a switch in other languages, but just as limited. You can’t, for example, write >32. You must use $_>32 and that point you might as well be using if else.
That’s fair. But the vast majority of the time, you’re either matching against a fixed set of strings (use a hash lookup), a fixed set of regular expressions (use the topicalized
forI mention), or you’re setting the same variable based on a decision tree (use a tabular ternary as Conway covers in Perl Best Practices).Which I agree with. Which leads me to believe that the smart match technology that was a big part of given when was an overreach and the main reason that given/when is still experimental. Would have been nice I’ve given when was just a switch and then smart match could have been added later.
I think part of it was driven by Perl 6 (now Raku) envy.
I know that given/when and smartmatch have already been deprecated some years ago and I think they have been removed from the language now.
They’re still in 5.32.1 and I haven’t seen them deprecated in any perldelta docs, just discussed in p5p.