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.

6 thoughts on “Switching up my switches in Perl

  1. 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 for I 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.

  2. 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.

Comments are closed.