4

In my perl code I've previously used the following two styles of writing which I've later found are being discouraged in modern perl:

# Style #1: Using & before calling a user-defined subroutine
&name_of_subroutine($something, $something_else);

# Style #2: Using ($$) to show the number of arguments in a user-defined sub
sub name_of_subroutine($$) {
  # the body of a subroutine taking two arguments.
}

Since learning that those styles are not recommended I've simply stopped using them.

However, out of curiosity I'd like to know the following:

  • What is the origin of those two styles of writing? (I'm sure I've not dreamt up the styles myself.)
  • Why are those two styles of writing discouraged in modern perl?
  • Have the styles been considered best practice at some point in time?
knorv
  • 49,059
  • 74
  • 210
  • 294
  • 1
    Everything already discussed on SO: http://stackoverflow.com/questions/2056904 http://stackoverflow.com/questions/297034 http://stackoverflow.com/questions/2485078 – daxim May 16 '10 at 15:45
  • 1
    possible duplicate of [Why are Perl function prototypes bad?](http://stackoverflow.com/questions/297034/why-are-perl-function-prototypes-bad) – Ether May 16 '10 at 16:42
  • 2
    It's ironic that you ask about these two common misuses of Perl features together, since the use of `&foo(@args)` overrides prototypes. – daotoad May 16 '10 at 18:24
  • 1
    I think it's an overstatement to say & is "discouraged in modern perl". Depends on who you let define "modern perl" for you. – ysth May 16 '10 at 21:01

3 Answers3

15

The & sigil is not commonly used with function calls in modern Perl for two reasons. First, it is largely redundant since Perl will consider anything that looks like a function (followed by parens) a function. Secondly, there is a major difference between the way &function() and &function are executed, which may be confusing to less experienced Perl programmers. In the first case, the function is called with no arguments. In the second case, the function is called with the current @_ (and it can even make changes to the argument list which will be seen by later statements in that scope:

sub print_and_remove_first_arg {print 'first arg: ', shift, "\n"}

sub test {
   &print_and_remove_first_arg;

   print "remaining args: @_\n";
}

test 1, 2, 3;

prints

first arg: 1
remaining args: 2 3

So ultimately, using & for every function call ends up hiding the few &function; calls which can lead to hard to find bugs. In addition, using the & sigil prevents the honoring of function prototypes, which can be useful in some cases (if you know what you are doing), but also may lead to hard to track down bugs. Ultimately, & is a powerful modifier to function behavior, and should only be used when that behavior is desired.

Prototypes are similar, and their use should be limited in modern Perl. What must be stated explicitly is that prototypes in Perl are NOT function signatures. They are hints to the compiler that tell it to parse calls to those functions in a similar way as the built in functions. That is, each of the symbols in the prototype tells the compiler to impose that type of context on the argument. This functionality can be very helpful when defining functions that behave like map or push or keys which all treat their first argument differently than a standard list operator would.

sub my_map (&@) {...}  # first arg is either a block or explicit code reference

my @ret = my_map {some_function($_)} 1 .. 10;

The reason sub ($$) {...} and similar uses of prototypes are discouraged is because 9 times out of 10 the author means "I want two args" and not "I want two args each with scalar context imposed on the call site". The former assertion is better written:

use Carp;
sub needs2 {
   @_ == 2 or croak 'needs2 takes 2 arguments';
   ...
}

which would then allow the following calling style to work as expected:

my @array = (2, 4);

needs2 @array;

To sum up, both the & sigil and function prototypes are useful and powerful tools, but they should only be used when that functionality is required. Their superfluous use (or misuse as argument validation) leads to unintended behavior and difficult to track down bugs.

Eric Strom
  • 39,821
  • 2
  • 80
  • 152
3

The & in function-calls was mandatory in Perl 4, so maybe you have picked that up from Programming perl (1991) by Larry Wall and Randal L. Schwartz, as I did, or somewhere similar.

As for the function prototypes, my guess is less qualified. Maybe you have been mimicking languages where it makes sense and/or is mandatory to declare argument lists, and since function prototypes in Perl look a little like argument lists, you've started adding them?

&function is discouraged because it makes the code less readable and isn't necessary (the cases that &function is necessary are rare and often better avoided).

Function prototypes aren't argument lists, so most of the time they'll just confuse your reader or lull you into a false sense of rigidity, so no need to use those unless you know exactly why you are.

& was mandatory in Perl 4, so they have been best/necessary practise. I don't think function prototypes ever has been.

asjo
  • 3,084
  • 2
  • 26
  • 20
  • 3
    You forgot to mention that using the sigil changes how the sub is called. `&foo( @args )` disables prototypes. `&foo` (no arguments) passes the current argument list (`@_`) directly to the subroutine. – daotoad May 16 '10 at 18:22
1

For style #1, the & before the subroutine is only necessary if you have a subroutine that shares a name with a builtin and you need to disambiguate which one you wish to call, so that the interpreter knows what's going on. Otherwise, it's equivalent to calling the subroutine without &.

Since that's the case, I'd say its use is discouraged since you shouldn't be naming your subroutines with the same names as builtins, and it's good practice to define all your subroutines before you call them, for the sake of reading comprehension. In addition to this, if you define your subroutines before you call them, you can omit the parentheses, like in a builtin. Plus, just speaking visually, sticking & in front of every subroutine unnecessarily clutters up the file.

As for function prototypes, they were stuck into Perl after the fact and don't really do what they were made to do. From an article on perl.com:

For the most part, prototypes are more trouble than they're worth. For one thing, Perl doesn't check prototypes for methods because that would require the ability to determine, at compile time, which class will handle the method. Because you can alter @ISA at runtime--you see the problem. The main reason, however, is that prototypes aren't very smart. If you specify sub foo ($$$), you cannot pass it an array of three scalars (this is the problem with vec()). Instead, you have to say foo( $x[0], $x[1], $x[2] ), and that's just a pain.

In the end, it's better to comment your code to indicate what you intend for a subroutine to accept and do parameter checking yourself. As the article states, this is actually necessary for class methods, since no parameter checking occurs for them.

For what it's worth, Perl 6 adds formal parameter lists to the language like this:

sub do_something(Str $thing, Int $other) {
    ...
}
v64
  • 320
  • 1
  • 4
  • 9
  • 2
    You don't have to include the & when you make a subroutine call prior to its definition in the file. The subroutine namespace is populated at compile time. – Brock May 16 '10 at 13:07
  • You're right, I was actually thinking of the ability to omit parentheses when putting the declaration before the invokation. "Omitting the ampersand" under http://oreilly.com/catalog/lperl3/chapter/ch04.html gives the rundown. – v64 May 16 '10 at 13:38
  • Isn't that why we have `CORE::func`? – rjh May 16 '10 at 13:55
  • 1
    That disambiguates in the other direction. – jrockway May 16 '10 at 14:02
  • Wrong on both counts. `&func` changes the way `func`'s parameters are interpreted, in a manner which is often undesired. Perl function prototypes do exactly what they were made to do, they're just named poorly because they're intended to do something *completely different* from what's called a function prototype in pretty much any other programming language. – Dave Sherohman May 17 '10 at 11:02