[ABE.pm] perl 5.10 rules! - a switch statement

Ricardo SIGNES rjbs-perl-abe at lists.manxome.org
Sun Jun 3 17:29:22 PDT 2007


Holy crap, the pigs just flew in from Hell, which had frozen over.  Perl is
getting a "switch" statement... but there's a twist.

Some languages have a statement that works like this:

  switch (variable) {
    case 1:
      do_something_one;
      break;
    case 2:
      do_something_2;
      break;
    default:
      do_default;
  }

It does just what you think.  I've left out the nuances that you probably know
about: leaving out 'break' to fall through or having multiple sequential cases
to let one bunch of code handle multiple cases.  In Perl, this sort of thing is
really easy to do with a hash, using a dispatch:

  %dispatch = (
    1 => \&do_something_one,
    2 => \&do_something_2,
    default => \&do_default;
  );

  $handler = $dispatch{ $variable } || $dispatch{ default };
  $handler->();

So, when people clamor for a switch statement, the traditional response has
been, "Jeez, people.  Write Perl, not C."  Why, then, is one being added?
Because it's way, way more powerful than a C switch statement, and a mere
dispatch table wouldn't work.

With a name stolen from Perl 6, it's called "given."  Our boring
switch/dispatch from above would be:

  given ($variable) {
    when (1) { do_something_one; } # if $variable == 1
    when (2) { do_something_2;   } # if $variable == 2
    default  { do_default; }
  }

...but how about some additional awesome magic?

  given ($variable) {
    when (1) { # $variable == 1
      do_something_one;
    }

    when (@edge_cases) { # if $variable in @edge_cases
      do_edge_case_handler;
    }

    when ($_ > 10e6) { # if $variable is over a million
      do_huge_handler;
    }

    when (\&test) { # if test($_) is true
      say "the test was true!";
      continue;
    }

    default {
      default_handler;
    }
  }

Woah!  There's magic for all manner of when check, and they all pretty much
make sense.

Want to know what's cooler?  You can use them outside of a given/when
structure.  Let's say you used to say something like:

  die "We don't support your browser!" if $user_agent eq $unsupported_browser;

...then later you actually have a bunch of browsers you don't work on, so you
start passing in an aarrayref instead of a string:

  die "We don't support your browser!"
    if grep { $user_agent eq $_ } @$unsupported_browser;

...and then finally you're reduced to passing in a subref that does a lookup
based on the latest user-agent strings known:

  die "We don't support your browser!" if $unsupported_browser->($user_agent);

In 5.10, you could have solved this problem at the outside by writing:

  die "We don't support your browser!" if $user_agent ~~ $unsupported_browser;

When you started out, you'd have put a string in there, and it would use eq.
Later, you'd put in an arrayref, and it would check whether $user_agent was in
the arrayref.  Finally, you'd put in a coderef and it would call it.  By saying
that something is a "smart match" target, rather than something more specific,
you can make your code very flexible without writing a lot of polymorphism on
your own.

The full table of how ~~ works can be found in perl 5.9.5's perlsyn page:

  http://search.cpan.org/~rgarcia/perl/pod/perlsyn.pod#Smart_matching_in_detail

-- 
rjbs


More information about the ABE-pm mailing list