Switching up my switches in Perl

Pretty soon after I start­ed writ­ing Perl in 1994, I noticed that it lacked a con­struct often found in oth­er lan­guages: the switch state­ment. Not to wor­ry, though—you can achieve the same effect with a cas­cad­ing series of if-elsif state­ments, right?

Well, no, you should­n’t do that, espe­cial­ly if the chain is super-​long. There’s even a perl­crit­ic pol­i­cy about it, which sug­gests that you use given and when instead.

But given and when (and the smart­match oper­a­tor they imply, ~~) are still exper­i­men­tal, with behav­ior sub­ject to change. So, what’s a respon­si­ble devel­op­er to do?

The answer is to use the for state­ment as a top­i­cal­iz­er, which is a fan­cy way of say­ing it assigns its expres­sion to $_. You can then use things that act on $_ to your heart’s con­tent, like reg­u­lar expres­sions. Here’s an example:

for ($foo) {
    /^abc/ and do {
        ...
        last;
    };
    /^def/ and do {
        ...
        last;
    };
    # FALL THRU
    ...
}

This will cov­er a lot of cas­es (ha-​ha, see what I did there? A lot of lan­guages use a case state­ment… oh, nev­er mind.) And if all you’re doing is exact string match­ing, there’s no need to bring in reg­ex­ps. You can use a hash as a lookup table:

my %lookup = (
    foo => sub { ... },
    bar => sub { ... },
);
$lookup{$match}->();

EDIT: If every alter­na­tive is an assign­ment to the same vari­able, a ternary table is anoth­er pos­si­bil­i­ty. This is a chained set of ternary con­di­tion­al (? :) oper­a­tors arranged for read­abil­i­ty. I first heard about this tech­nique 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 inef­fi­cient as a cascaded-if/​elsif, it’s clear­er that it’s a sin­gle assign­ment. It’s also more com­pact and reads like a table with columns of match­es and alternatives.

Any of these pat­terns are prefer­able to cas­cad­ing if/​elsifs. And if you want to check the devel­op­ment of given, when, and ~~, see this issue on GitHub. The last com­ment was eight years ago, though, so I would­n’t hold my breath.


Discover more from The Phoenix Trap

Subscribe to get the latest posts sent to your email.

Mark Gardner Avatar

Hi, I’m Mark.

Hi, I’m Mark Gard­ner, and this is my personal blog. I show software developers how to level up by building production-ready things that work. Clear code, real projects, lessons learned.

Comments

6 responses to “Switching up my switches in Perl”

  1. Matthew Persico Avatar
    Matthew Persico

    I’ve found this to be no more com­pli­cat­ed than a switch in oth­er lan­guages, but just as lim­it­ed. You can’t, for exam­ple, write >32. You must use $_>32 and that point you might as well be using if else.

    1. Mark Avatar

      That’s fair. But the vast major­i­ty of the time, you’re either match­ing against a fixed set of strings (use a hash lookup), a fixed set of reg­u­lar expres­sions (use the top­i­cal­ized for I men­tion), or you’re set­ting the same vari­able based on a deci­sion tree (use a tab­u­lar ternary as Conway cov­ers in Perl Best Practices).

      1. Matthew Persico Avatar
        Matthew Persico

        Which I agree with. Which leads me to believe that the smart match tech­nol­o­gy that was a big part of giv­en when was an over­reach and the main rea­son that given/​when is still exper­i­men­tal. Would have been nice I’ve giv­en when was just a switch and then smart match could have been added later.

        1. Mark Avatar

          I think part of it was dri­ven by Perl 6 (now Raku) envy.

  2. Jim Bacon Avatar
    Jim Bacon

    I know that given/​when and smart­match have already been dep­re­cat­ed some years ago and I think they have been removed from the lan­guage now.

    1. Mark Avatar

      They’re still in 5.32.1 and I haven’t seen them dep­re­cat­ed in any perldelta docs, just dis­cussed in p5p.

To respond on your own website, enter the URL of your response which should contain a link to this post's permalink URL. Your response will then appear (possibly after moderation) on this page. Want to update or remove your response? Update or delete your post and re-enter your post's URL again. (Find out more about Webmentions.)