Subroutines and Ampersands

I’ve had this discussion several times recently, so I thought it was worth writing a blog post so that I have somewhere to point people the next time it comes up.

Using ampersands on subroutine calls (&my_sub or &my_sub(...)) is never necessary and can have potentially surprising side-effects. It should, therefore, never be used and should particularly be avoided in examples aimed at beginners.

Using an ampersand when calling a subroutine has three effects.

  1. It disambiguates the code so the the Perl compiler knows for sure that it has come across a subroutine call.
  2. It turns off prototype checking.
  3. If you use the &my_sub form (i.e. without parentheses) then the current value of @_ is passed on to the called subroutine.

Let’s look at these three effects in a little more detail.

Disambiguating the code is obviously a good idea. But adding the ampersand is not the only way to do it. Adding a pair of parentheses to the end of the call (my_sub()) has exactly the same effect. And, as a bonus, it looks the same as subroutine calls do in pretty much every other programming language ever invented. I can’t think of a single reason why anyone would pick &my_sub over my_sub().

I hope we’re agreed that prototypes are unnecessary in most Perl code (perhaps that needs to be another blog post at some point). Of course there are a few good reasons to use them, but most of us won’t be using them most of the time. If you’re using them, then turning off prototype checking seems to be a bad idea. And if you’re not using them, then it doesn’t matter whether they’re checked or not. There’s no good argument here for  using ampersands.

Then we come to the invisible passing of @_ to the called subroutine. I have no idea why anyone ever thought this was a good idea. The perlsub documentation calls it “an efficiency mechanism” but admits that is it one “that new users may wish to avoid”. If you want @_ to be available to the called subroutine then just pass it in explicitly. Your maintenance programmer (and remember, that could be you in six months time) will be grateful and won’t waste hours trying to work out what is going on.

So, no, there is no good reason to use ampersands when calling subroutines. Please don’t use them.

There is, of course, one case where ampersands are still useful when dealing with subroutines – when you are taking a reference to an existing, named subroutine. But that’s the only case that I can think of.

What do you think? Have I missed something?

It’s unfortunate that a lot of the older documentation on CPAN (and, indeed, some popular beginners’ books) still perpetuate this outdated style. It would be great if we could remove it from all example code.

Update: In the comments, a few people question my description of the &my_sub mechanism of passing on @_ to the called subroutine as being pointless. And, yes, they’re right. There are some places where that mechanism is useful. But I think that if you’re at the stage where you’re using tail-recursion, continuations or co-routines then I think you’re quite capable of understanding the limitations of the advice in this article.

I stand by my conclusion that in most cases, &my_sub should be replaced with my_sub().

16 thoughts on “Subroutines and Ampersands

  1. You need the ampersand if you want to do tail call optimization. With a “goto &func” you directly jump to the function without that the stack gets bigger. And then you also need the feature that “@_” is (re-)used as the current arguments.

    Well a good tail call optimization in Perl 5 would be better. And probably it would also be better to re-write such functions in an iterative way.

  2. Saying never use ampersands overlooks one important use, that is searching through modified code to see if a subroutine is called. It’s easy to search for “&updatedata” but “updatedata” doesn’t give the required check. Just a thought from old school.

    1. I’m not convinced by that argument, I’m afraid. Unless you have very generic subroutine names, then just searching for “updatedata” is going to give you very few false positives. And you can decrease that number even more by searching for “updatedata(”.

  3. What do you think about subroutine references? I often create iterators that take no arguments. &$iter is shorter than $iter->()

    1. It’s undoubtedly shorter. But is shorter code really what we should be aiming at? I’d use $iter->() because I think it’s more readable.

  4. I posted this to the sf mongers list recently:
    The idea was that just like $_ works as a kind of singular
    pronoun in perl, @_ was supposed to work as a plural pronoun.
    They figured you might want to write code like this:

    sub do_stuff {
    &check_numerics;
    &discard_out_of_range;
    $sum = &total_up;
    }

    And just as an aside, I think it’s interesting that pretty much
    none of us want to write code like that, but all of us make use
    of $_ all the time. Consistency always seems nice in theory, but
    in practice it seems that different cases are often really
    different…

  5. I never really make use of “$_”. I think $_ should also be banned, i only use $_ if i have to. For example in map, grep and so on where it is not possible to avoid it. I think $_ has the same restrictions as &. It is not clearer as directly passing a variable name, it can be unclear when/if a function uses $_, and there also can be side-effect because $_ is a global-variable.

    I would say the same to $_. Avoid it whenever it is possible.

  6. “i only use $_ if i have to. For example in map, grep and so on where it is not possible to avoid it. ”

    If you think those are minor cases, maybe you’re not using them enough.

    My rule of thumb is to avoid assigning to $_ yourself (and if you do, you should remember to localize it, just-in-case).

    Again, the standard advice is to think of it as a pronoun. If you’ve just used someone’s full name a moment ago, you can say “he” or “she” without much fear of confusion; in a longer text (or a story with a large cast of characters, etc) you need to be careful with it.

  7. Except, “goto &func” is actually a lot slower than “return &func” – as long as you don’t run out of stack… Irritating but unfortunately true.

    Anyway, yeah. “I have no idea why anyone ever thought this was a good idea” is basically saying “I have never heard of continuations or coroutines or anything of the sort”, or hell, just to stay with contemporarily culturally proper Perl, “I didn’t realize there were uses for method modifiers”.

    So this article is pretty useless. It says something true: you almost never want to use ampersands. But the reasons given for why not boil down to “it looks weird and does something whose use case I don’t understand”. That’s not a reason, that’s just a cultural norm. If someone didn’t know why to avoid ampersands, I wouldn’t want them to start avoiding those after reading this article either – because they’d have no idea why they’re doing what they’re doing.

  8. One exception to “it’s never necessary” — sometimes some other joker used prototypes when they shouldn’t have (they almost always shouldn’t have), and you can’t change their code. But you *can* use the & to override the prototype if you know what you’re doing and get sane behavior.

  9. > Then we come to the invisible passing of @_ to the called subroutine. I have no idea why anyone ever thought this was a good idea. The perlsub documentation calls it “an efficiency mechanism” but admits that is it one “that new users may wish to avoid”. If you want @_ to be available to the called subroutine then just pass it in explicitly. Your maintenance programmer (and remember, that could be you in six months time) will be grateful and won’t waste hours trying to work out what is going on.

    Your update, even, seems to miss a lot of the point of this technique: A copy of the entire @_ is not needed (which could be an important optimization), and the called subroutine can modify @_ *for the calling subroutine*. use Data::Dumper; sub a { &b; print Dumper(\@_); } sub b { shift; } a(1,2,3);

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.