I am in the middle of a project at my job where we are converting some Perl to Python :-(. In the conversion I was explaining this invocation of a switch statement:
for (ref($thing)) {
    /ARRAY/ && do {
        some_array_thing($thing);
        last;
    };
    /LIST/ && do {
        some_list_thing($thing);
        last;
    };
    ## default;
    some_scalar_thing($thing);
}
After explaining how the for statement sets $_, I was asked, "Why not just set $_?"
Indeed, why not? You can play with the following code here: http://tpcg.io/KN9H82
use strict;
use warnings;
{
    local $_ = 'foo';
    /bar/ && do { print 'we got bar'; last };
    /foo/ && do { print 'we got foo'; last };
    /eek/ && do { print 'we got eek'; last };
    print 'we dropped through';
}
with output
we got foo
So, my questions are:
- Why have I never seen this in any discussion of Perl's switchsynonyms?
- Is there any inherent problem with this?
 

 
    
Top comments (14)
Do you know Syntax::Keyword::Match? If you are interested in switch statement, you can get inspiration from this module.
Not exactly what I'd want. I'd rather see:
If you want to write so, Syntax::Keyword::Match is insufficient.
No I did not, but I will investigate. Thank you.
That’s exactly what
fordoes, except it’s lexical instead oflocal.Perl v5.10 introduced support for explicitly lexical
$_, but it was removed in v5.24 after being relegated to experimental status in v5.18. (See that last link for the issues it introduced.)Right, but the Python person I was talking to said that for their reading, the explicit assignment to
$_made a lot more sense to him than implying it from aforloop. Again, a Python person, "explicit is better than implicit". However, to be honest, I like explicit setting of$_better than a for loop. So much cleaner IFF you really are using only one value. In fact, I would improve this code withAnd I think I avoid the lexical issues with the
localmodifier, yes?Sure, use a label and then the last statement of your conditions can say
last SWITCH;.If you like your Perl more “Pythonic” then go for it. Personally I prefer to adopt language idioms rather than adapt one language to another. Similar arguments have occurred about C-style
for ( ; ; )loops vs.foreachloops that eschew an index variable when it’s not necessary.There’s an old saying: “You can write FORTRAN in any language.” The Sapir-Whorf hypothesis says that language influences thought. Limiting yourself to the idioms of one language means limiting yourself to solutions that can be expressed in it, and that doesn’t make sense if you’re actually using a different language.
Agreed. But some idioms can be improved. I think the explicit
$_assignment is better than the side-effect assignment via afor (each)statement that has only one target. And yes, in the production code, I added aSWITCH:label. However I did not changelast;to 'last SWITCH;; I think that's too verbose, and it doesn't impedance-match withbreakin traditionalswitch` statements.The issue is not that it's lexical, but that assigning to a lexical
$_instead of the superglobal$_broke assumptions that people make that$_can be used in other code scopes dynamically, such as subroutines you call. If it had started out being lexically aliased byforeachandmapit would lead to safer code overall.localassignment does avoid some danger of action at a distance from aliasing withforeach; in the following code, if some_sub were to assign anything to$_it would clobber$varas well.In the end I always recommend using a few more keystrokes and avoiding
$_for this entirely.I whipped up two quick tests:
gist.github.com/matthewpersico/aa1... and gist.github.com/matthewpersico/aa1... and yes, not having that
localcan be disastrous. So, sinceforandmapand the like are coded "properly", i.e., theylocalize$_, you should be safe to use them and use functions that call them. But if someone has done this$_assignment trick and forgets thelocal, there be dragons.github.com/cpanery/venus/blob/mast...
Oh, I'll bite (resisting the pun): Why "Venus"?
I was going to say that you can't get fall-through with
/match/ && do {...}, but then I started thinking "When was the last time I wanted fall-through? That wasn't parlor-trick code?"You get fall-through if you omit
last.