Another glorious day in Advent; another gift awaits us. It’s switch statements!
Well, the term for them is still “switch statement” in Perl 6, but the keyword has changed for linguistic reasons. It’s now given
, as in “given today’s weather”:
given $weather { when 'sunny' { say 'Aah! ☀' } when 'cloudy' { say 'Meh. ☁' } when 'rainy' { say 'Where is my umbrella? ☂' } when 'snowy' { say 'Yippie! ☃' } default { say 'Looks like any other day.' } }
Here’s a minimal explanation of the semantics, just to get us started: in the above example, the contents of the variable $weather
is tested against the strings 'sunny'
, 'cloudy'
, 'rainy'
, and 'snowy'
, one after the other. If either of them matches, the corresponding block runs. If none matches, the default
block triggers instead.
Not so different from switch statements in other languages, in other words. (But wait!) We’ll note in passing that the when
blocks don’t automatically fall through, so if you have several conditions which would match, only the first one will run:
given $probability { when 1.00 { say 'A certainty' } when * > 0.75 { say 'Quite likely' } when * > 0.50 { say 'Likely' } when * > 0.25 { say 'Unlikely' } when * > 0.00 { say 'Very unlikely' } when 0.00 { say 'Fat chance' } }
So if you have a $probability
of 0.80
, the above code will print Quite likely
, but not Likely
, Unlikely
etc. (In the cases when you want to “fall through” from a when
block, you can end it with the keyword continue
.) (Update: after spec discussions that originated in the comments of this post, break
/continue
were renamed to succeed
/proceed
.)
Note that in the above code, strings and decimal numbers and comparisons are used as the when
expression. How does Perl 6 know how to match the given
value against the when
value, when both can be of wildly varying types?
The answer is that the two values enter a negotiation process called smartmatching, mentioned briefly in Day 13. To summarize, smartmatching (written as $a ~~ $b
) is a kind of “regex matching on steroids”, where the $b
doesn’t have to be a regex, but can be of any type. For ranges, the smartmatch will check if the value we want to match is within the range. If $b
is a class or a role or a subtype, the smartmatch will perform a type check. And so on. For values like Num
and Str
which represent themselves, some appropriate equivalence check is made.
The “whatever star” (*
) has the peculiar property that it smartmatches on anything. Oh, and default
is just sugar for when *
.
To summarize the summary, smartmatching is DWIM in operator form. And the given
/when
construct runs on top of it.
Now for something slightly head-spinning: the given
and when
features are actually independant! While you complete the syllable “WHAT?”, let me explain how.
Given is actually a sort of once-only for
loop.
given $punch-card { .bend; .fold; .mutilate; }
See what happened there? All given
does is set the topic, also known to Perl people as $_
. The cute .method
is actually short for $_.method
.
Now it’s easier to see how when
can be used without a given
, too. when
can be used inside any block which sets $_
, implicitly or explicitly:
my $scanning; for $*IN.lines { when /start/ { $scanning = True } when /stop/ { $scanning = False } if $scanning { # Do something which only happens between the # lines containing 'start' and 'stop' } }
Note that those when
blocks exhibit the same behaviour as the in a given
block: they skip the rest of the surrounding block after executing, which in the above code means they go directly to the next line in the input.
Here’s another example, this time with $_
explicitly set:
sub fib(Int $_) { when * < 2 { 1 } default { fib($_ - 1) + fib($_ - 2) } }
(This independence between given
and when
plays out in other ways too. For example, the way to handle exceptions is with a CATCH
block, a variant of given
which topicalizes on $!
, the variable holding the most recent exception.)
To top it all off, both given
and when
come in statement-ending varieties, just as for
, if
and the others:
say .[0] + .[1] + .[2] given @list;
say 'My God, it's full of vowels!' when /^ <[aeiou]>+ $/;
You can even nest a when
inside a given
:
say 'Boo!' when /phantom/ given $castle;
As given
and when
represent another striking blow against the Perl obfuscation track record, I hereby present you with the parting gift of an obfu DNA helix, knowing full well that it doesn’t quite make up for the damage caused. :)
$ perl6 -e 'for ^20 {my ($a,$b)=<AT CG>.pick.comb.pick(*);\ my ($c,$d)=sort map {6+4*sin($_/2)},$_,$_+4;\ printf "%{$c}s%{$d-$c}s\n",$a,$b}' G C TA C G G C C G G C T A CG CG C G T A T A T A C G TA T A T A A T C G G C