5 Replies - 1637 Views - Last Post: 29 March 2012 - 01:30 AM

#1 cupidvogel  Icon User is offline

  • D.I.C Addict

Reputation: 31
  • View blog
  • Posts: 593
  • Joined: 25-November 10

Anomaly with Perl Prototypes

Posted 26 March 2012 - 11:16 AM

Hi, the following code uses prototypes to let me enjoy parentheses-less functions. I intend to pass only two parameters from the list defined after the function invocation (without parentheses). Since I have set the prototype to $$ in the function definition, I guessed the correct output should be (4,5,6). But what I am getting is an error message like this:

Too many arguments for main::coo at sr3.pl line 7, near "4)"
Execution of sr3.pl aborted due to compilation errors.



Can anyone spot the bug? The code is as follows:

sub coo($$) { return @_ + (1,2); }
@d = (coo 1,2,3,4);



Is This A Good Question/Topic? 0
  • +

Replies To: Anomaly with Perl Prototypes

#2 dsherohman  Icon User is offline

  • Perl Parson
  • member icon

Reputation: 226
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Anomaly with Perl Prototypes

Posted 27 March 2012 - 02:28 AM

View Postcupidvogel, on 26 March 2012 - 07:16 PM, said:

Hi, the following code uses prototypes to let me enjoy parentheses-less functions. I intend to pass only two parameters from the list defined after the function invocation (without parentheses). Since I have set the prototype to $$ in the function definition, I guessed the correct output should be (4,5,6). But what I am getting is an error message like this:

Too many arguments for main::coo at sr3.pl line 7, near "4)"
Execution of sr3.pl aborted due to compilation errors.



Can anyone spot the bug? The code is as follows:

sub coo($$) { return @_ + (1,2); }
@d = (coo 1,2,3,4);



My first reaction was "the bug is that you're passing too many arguments". Your ($$) prototype specifies that you must pass exactly two arguments and you're trying to pass four. I'm sure there's a variation on prototype syntax which will let you specify a variable number of arguments, but I don't know it and I'm not going to dig it up because...

My second reaction was "the bug is that you're using prototypes". Unless you really, really want to coerce your arguments into a specific type - and you both recognize and accept the unexpected behavior which will result[1] - they're not something you want to use. They're not needed for your stated purpose anyhow. perl -E 'sub foo { say @_ }; foo 1, 2, 3, 4;' works just fine, printing 1234. (Perl only requires parentheses for user-defined function calls if the compiler hasn't seen the function yet, since it doesn't know it's a function in that case; whether you use prototypes or not has no effect on this.)

The real bug, though, is that + is not a list operator. It forces scalar context on its arguments, so the result of your coo function will be the number of arguments plus 2 - in scalar context, an array becomes the number of items in the array and a literal list becomes the last item in the list (in this case, 2), so those are the numbers that will be added together.


[1] e.g., @arr = (1, 2); coo @arr; will not work, it will die with "too few arguments" because, instead of getting the arguments 1, 2, the prototype will coerce a reference to @arr into the first argument, then have nothing left to fill the second argument.
Was This Post Helpful? 0
  • +
  • -

#3 cupidvogel  Icon User is offline

  • D.I.C Addict

Reputation: 31
  • View blog
  • Posts: 593
  • Joined: 25-November 10

Re: Anomaly with Perl Prototypes

Posted 27 March 2012 - 03:14 AM

I don't think that variable number of parameters requires anything extra. Consider the following code:

sub foo($) { print "Number of arguments: ".@_; }
foo 3,4,5;  ##prints 1



It prints out 1, whereas

sub foo { print "Number of arguments: ".@_; }
foo 3,4,5;  ##prints 3



prints out 3. The $ prototype ensures that only the first value from the list specified after the function invocation (without parentheses) is taken in as parameter, rest are discarded (or appended to an array if the function returns some value and is called in a list context). So the case for using $$ should be similar, isn't it? And as for the return value, yeah I know I erred, but I never bothered rectifying it, for that was just a dummy return statement to return something. I might as well have returned 456!
Was This Post Helpful? 0
  • +
  • -

#4 dsherohman  Icon User is offline

  • Perl Parson
  • member icon

Reputation: 226
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Anomaly with Perl Prototypes

Posted 28 March 2012 - 02:17 AM

View Postcupidvogel, on 27 March 2012 - 11:14 AM, said:

So the case for using $$ should be similar, isn't it?


Apparently not:
$ perl -E 'sub foo($) { say "Number of arguments: ".@_; } foo 3,4,5;'
Number of arguments: 1
$ perl -E 'sub foo($$) { say "Number of arguments: ".@_; } foo 3,4,5;'
Too many arguments for main::foo at -e line 1, near "5;"
Execution of -e aborted due to compilation errors.
$ perl -E 'sub foo($$$) { say "Number of arguments: ".@_; } foo 3,4,5;'
Number of arguments: 3
$ perl -E 'sub foo($$$) { say "Number of arguments: ".@_; } foo 3,4,5,6;'
Too many arguments for main::foo at -e line 1, near "6;"
Execution of -e aborted due to compilation errors.



Looks like it may be a special case for a single $, with $$/$$$ requiring an exact match in the number of arguments. One more "quirk" of prototypes to support avoiding them if at all possible...

(far too many minutes later)

OK, I've tracked down and attempted to comprehend Tom Christiansen's Far More than Everything You've Ever Wanted to Know about Prototypes in Perl and, if I'm understanding it correctly, it is indeed a special case. Perl treats "void input" (zero-argument), "monadic" (one-argument), and "variadic" (more-than-one-argument) prototypes differently. I've done a little testing and verified that, at least on my perl (This is perl, v5.10.1 (*) built for i686-linux-gnu-thread-multi), void input and variadic prototypes require the number of arguments passed to be exactly the same as the number in the prototype, while monadic prototypes will take only the first argument and leave the rest alone.

In any case, the second point in my earlier reply still stands: You said that you wanted to use a prototype to allow you to call the function without needing parentheses, but you can do that by default. If they don't do what you wanted them for and they cause strange errors, why not just drop them? They're not doing you any good - see Gratuitous use of Perl Prototypes for a quick debunking of the normally-claimed advantages that people expect to get from prototypes.

You might also want to take a look at The Perl Cookbook's section on "Prototyping Functions", as it includes a discussion specifically on "Omitting parentheses", which shows examples of how, if you use prototypes, "in the absence of parentheses, you cannot know what is going on by casual inspection. Things that look the same can quietly behave completely differently from one another... This is one strong argument for using more parentheses than might be demanded by purely precedential concerns (or, alternatively, this is an argument for avoiding prototypes)." (emphasis in original) Seems a little counterproductive if you use prototypes in an attempt to avoid parentheses and end up needing extra parentheses because of the prototypes.
Was This Post Helpful? 1
  • +
  • -

#5 cupidvogel  Icon User is offline

  • D.I.C Addict

Reputation: 31
  • View blog
  • Posts: 593
  • Joined: 25-November 10

Re: Anomaly with Perl Prototypes

Posted 28 March 2012 - 03:59 AM

First of all, tons of thanks for explaining at such length. I have read the chapter on Prototypes from Perl CookBook (in fact that is where I first found that there exists something like prototype, for Learning Perl didn't once mention it), but the other link you have provided is really awesome. I don't intend to use prototypes in my code, I just want to know all its arcane nuances, that's why I was testing with this code. So you are right insofar as there is hardly any meaning in using prototype avoid to avoid parentheses whereas I have to use them ultimately to delimit the array. As for the variaidc bit you mentioned, if I declare the function as

function foo(\@@) {
print @{$_[0]}; ##prints 1 2 3
print "$_[1] $_[2] $_[3]"; ##prints 4 5 6
}
@a = (1,2,3);
foo(@a,4,5,6);



the output is as shown, but if what you said is true, then this can't be, right? for I am passing 4 arguments whereas only 2 are mentioned in the parameter list.

This post has been edited by cupidvogel: 28 March 2012 - 11:11 AM

Was This Post Helpful? 0
  • +
  • -

#6 dsherohman  Icon User is offline

  • Perl Parson
  • member icon

Reputation: 226
  • View blog
  • Posts: 654
  • Joined: 29-March 09

Re: Anomaly with Perl Prototypes

Posted 29 March 2012 - 01:30 AM

It's entirely possible that I could be wrong. :D I don't think I've ever used prototypes myself, so my last reply here was just a mix of what I could deduce from the code samples you're posting and my understanding of Christiansen's article.

That said... @ is not $. It makes perfect sense to me that a prototype of @ would accept any number of arguments, while $ would insist on getting one and only one - an array can hold any number of elements and a scalar holds one and only one value.

Although, given that a $ prototype coerces array arguments into arrayrefs rather than accepting an individual element from the array, I'm a little surprised that an @ prototype will accept a list of individual values instead of requiring them to be put into an array variable first. But, then, Perl has never been shy about being inconsistent for the sake of doing what seems likely to be the programmer's intent...
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1