views:

55

answers:

1

The new method of Parse::RecDescent has this prototype:

sub new ($$$)
{
   # code goes here
}

and if I create an object like this:

my $parser = Parse::RecDescent->new($grammar);

it will create a parser, and the method will receive 2 parameters "Parse::RecDescent" and $grammar, right? If I try to create an object like:

Parse::RecDescent::new("Parse::RecDescent",$grammar)

this will fail saying "Not enough arguments for Parse::RecDescent::new", and I understand this message. I'm only passing 2 parameters. However, I don't understand why the arrow version works.

Can you explain?

+11  A: 

Function prototypes are not checked when you call it as an OO-style method. In addition, you bypass prototype checking when you call a sub with &, as in &sub(arg0, arg1..);

From perldoc perlsub:

Not only does the "&" form make the argument list optional, it also disables any prototype checking on arguments you do provide. This is partly for historical reasons, and partly for having a convenient way to cheat if you know what you're doing. See Prototypes below.

Method calls are not influenced by prototypes either, because the function to be called is indeterminate at compile time, since the exact code called depends on inheritance.

While Parse::RecDescent::new("Parse::RecDescent", $grammar) is syntactically correct, that's a pretty smelly way of calling the constructor, and now you are forcing it to be defined in that class (rather than in an ancestor). If you really need to validate your arguments, do it inside the method:

sub new
{
    my ($class, @args) = @_;
    die "Not enough arguments passed to constructor" if @args < 2;
    # ...
}

See also this earlier question on prototypes and why they aren't usually such a great idea.

Ether