perl constructor keyword 'new'
Asked Answered
S

2

9

I am new to Perl and currently learning Perl object oriented and came across writing a constructor. It looks like when using new for the name of the subroutine the first parameter will be the package name.

Must the constructor be using the keyword new? Or is it because when we are calling the new subroutine using the packagename, then the first parameter to be passed in will be package name?

packagename->new;

and when the subroutine have other name it will be the first parameter will be the reference to an object? Or is it because when the subroutine is call via the reference to the object so that the first parameter to be passed in will be the reference to the object?

$objRef->subroutine;
Sanjay answered 8/6, 2014 at 12:32 Comment(3)
You might benefit from reading 'Modern Perl' if you have not already. modernperlbooks.com/books/modern_perl_2014Shadowy
There is no new keyword in Perl. It is a common name for an object constructor and is used that way by many classes, but it is by no means required.Runic
Yes, you are right: the invocant is always the first argument (or zeroth) under method invocation, no matter the invocation method.Runic
R
7

It is not first parameter to new, but indirect object syntax,

perl -MO=Deparse -e 'my $o = new X 1, 2'

which gets parsed as

my $o = 'X'->new(1, 2);

From perldoc,

Perl suports another method invocation syntax called "indirect object" notation. This syntax is called "indirect" because the method comes before the object it is being invoked on.

That being said, new is not some kind of reserved word for constructor invocation, but name of method/constructor itself, which in perl is not enforced (ie. DBI has connect constructor)

Racquelracquet answered 8/6, 2014 at 12:46 Comment(7)
so the first parameter will be 'X' which should be the package name for the subroutine new because the the new subroutine is invoked using 'X'?Sanjay
@Sanjay yes, but notice that only 1 and 2 are parameters.Racquelracquet
The function called here is, modulo inheritance, going to be X::new("X", 1, 2) and so will receive the invocant as its zeroth argument. Also, that brief synopsis about indirect-object method invocation is probably insufficient for anyone to know what it is actually meaning. Please see the perlglossary(1) man page.Runic
This is about dative objects in English: indirect object: In English grammar, a short noun phrase between a verb and its direct object indicating the beneficiary or recipient of the action. In Perl, print STDOUT "$foo\n"; can be understood as “verb indirect-object object” where STDOUT is the recipient of the print action, and $foo is the object being printed. Similarly, when invoking a method, you might place the invocant in the dative slot between the method and its arguments: $gollum = new Pathetic::Creature "Sméagol"; give $gollum "Fisssssh!"; give $gollum "Precious!";Runic
indirect object slot: The syntactic position falling between a method call and its arguments when using the indirect object invocation syntax. (The slot is distinguished by the absence of a comma between it and the next argument.) STDERR is in the indirect object slot here: print STDERR "Awake! Awake! Fear, Fire, Foes! Awake!\n";Runic
@Runic tnx for pointing out parallels with natural language, and feel free to improve the answer. Personally I'm not a fan of indirect syntax as perl OO alone seems like unfortunate historic artefact.Racquelracquet
I’m certainly not going to go out of my way to defend dative-style (positionally based) method invocation in Perl. I do have a problem with the nomenclature, though, because it runs counter to traditional naming of these matters.Runic
R
14

NB: All examples below are simplified for instructional purposes.

On Methods

Yes, you are correct. The first argument to your new function, if invoked as a method, will be the thing you invoked it against.

There are two “flavors” of invoking a method, but the result is the same either way. One flavor relies upon an operator, the binary -> operator. The other flavor relies on ordering of arguments, the way bitransitive verbs work in English. Most people use the dative/bitransitive style only with built-ins — and perhaps with constructors, but seldom anything else.

Under most (but not quite all) circumstances, these first two are equivalent:

1. Dative Invocation of Methods

This is the positional one, the one that uses word-order to determine what’s going on.

use Some::Package;
my $obj1 = new Some::Package NAME => "fred"; 

Notice we use no method arrow there: there is no -> as written. This is what Perl itself uses with many of its own functions, like

 printf STDERR "%-20s: %5d\n", $name, $number;

Which just about everyone prefers to the equivalent:

 STDERR->printf("%-20s: %5d\n", $name, $number);

However, these days that sort of dative invocation is used almost exclusively for built-ins, because people keep getting things confused.

2. Arrow Invocation of Methods

The arrow invocation is for the most part clearer and cleaner, and less likely to get you tangled up in the weeds of Perl parsing oddities. Note I said less likely; I did not say that it was free of all infelicities. But let’s just pretend so for the purposes of this answer.

use Some::Package;
my $obj2 = Some::Package->new(NAME => "fred");

At run time, barring any fancy oddities or inheritance matters, the actual function call would be

 Some::Package::new("Some::Package", "NAME", "fred");

For example, if you were in the Perl debugger and did a stack dump, it would have something like the previous line in its call chain.

Since invoking a method always prefixes the parameter list with invocant, all functions that will be invoked as methods must account for that “extra” first argument. This is very easily done:

package Some::Package;
sub new {
   my($classname, @arguments) = @_;
   my $obj = { @arguments };
   bless $obj, $classname;
   return $obj;
}

This is just an extremely simplified example of the new most frequent ways to call constructors, and what happens on the inside. In actual production code, the constructor would be much more careful.

Methods and Indirection

Sometimes you don’t know the class name or the method name at compile time, so you need to use a variable to hold one or the other, or both. Indirection in programming is something different from indirect objects in natural language. Indirection just means you have a variable that contains something else, so you use the variable to get at its contents.

print 3.14;    # print a number directly

$var = 3.14;   # or indirectly
print $var;

We can use variables to hold other things involved in method invocation that merely the method’s arguments.

3. Arrow Invocation with Indirected Method Name:

If you don’t know the method name, then you can put its name in a variable. Only try this with arrow invocation, not with dative invocation.

use Some::Package;
my $action = (rand(2) < 1) ? "new" : "old";
my $obj    = Some::Package->$action(NAME => "fido");

Here the method name itself is unknown until run-time.

4. Arrow Invocation with Indirected Class Name:

Here we use a variable to contain the name of the class we want to use.

my $class = (rand(2) < 1) 
              ? "Fancy::Class" 
              : "Simple::Class";
my $obj3 = $class->new(NAME => "fred");

Now we randomly pick one class or another.

You can actually use dative invocation this way, too:

my $obj3 = new $class NAME => "fred";

But that isn’t usually done with user methods. It does sometimes happen with built-ins, though.

my $fh = ($error_count == 0) ? *STDOUT : *STDERR;
printf $fh "Error count: %d.\n", $error_count;

That’s because trying to use an expression in the dative slot isn’t going to work in general without a block around it; it can otherwise only be a simple scalar variable, not even a single element from an array or hash.

printf { ($error_count == 0) ? *STDOUT : *STDERR } "Error count: %d.\n", $error_count;

Or more simply:

print { $fh{$filename} } "Some data.\n";

Which is pretty darned ugly.

Let the invoker beware

Note that this doesn’t work perfectly. A literal in the dative object slot works differently than a variable does there. For example, with literal filehandles:

print STDERR;

means

print STDERR $_;

but if you use indirect filehandles, like this:

print $fh;

That actually means

print STDOUT $fh;

which is unlikely to mean what you wanted, which was probably this:

print $fh $_;

aka

$fh->print($_);

Advanced Usage: Dual-Nature Methods

The thing about the method invocation arrow -> is that it is agnostic about whether its left-hand operand is a string representing a class name or a blessed reference representing an object instance.

Of course, nothing formally requires that $class contain a package name. It may be either, and if so, it is up to the method itself to do the right thing.

use Some::Class;

my $class = "Some::Class";
my $obj   = $class->new(NAME => "Orlando"); 

my $invocant = (rand(2) < 1) ? $class : $obj;
$invocant->any_debug(1);

That requires a pretty fancy any_debug method, one that does something different depending on whether its invocant was blessed or not:

package Some::Class;
use Scalar::Util qw(blessed);

sub new {
   my($classname, @arguments) = @_; 
   my $obj = { @arguments };
   bless $obj, $classname;
   return $obj;
}   

sub any_debug {
    my($invocant, $value) = @_;
    if (blessed($invocant)) {
        $invocant->obj_debug($value);
    } else {
        $invocant->class_debug($value);
    }
}

sub obj_debug {
    my($self, $value) = @_;
    $self->{DEBUG} = $value;
}

my $Global_Debug;
sub class_debug {
    my($classname, $value) = @_;
    $Global_Debug = $value;
}

However, this is a rather advanced and subtle technique, one applicable in only a few uncommon situations. It is not recommended for most situations, as it can be confusing if not handled properly — and perhaps even if it is.

Runic answered 8/6, 2014 at 18:19 Comment(0)
R
7

It is not first parameter to new, but indirect object syntax,

perl -MO=Deparse -e 'my $o = new X 1, 2'

which gets parsed as

my $o = 'X'->new(1, 2);

From perldoc,

Perl suports another method invocation syntax called "indirect object" notation. This syntax is called "indirect" because the method comes before the object it is being invoked on.

That being said, new is not some kind of reserved word for constructor invocation, but name of method/constructor itself, which in perl is not enforced (ie. DBI has connect constructor)

Racquelracquet answered 8/6, 2014 at 12:46 Comment(7)
so the first parameter will be 'X' which should be the package name for the subroutine new because the the new subroutine is invoked using 'X'?Sanjay
@Sanjay yes, but notice that only 1 and 2 are parameters.Racquelracquet
The function called here is, modulo inheritance, going to be X::new("X", 1, 2) and so will receive the invocant as its zeroth argument. Also, that brief synopsis about indirect-object method invocation is probably insufficient for anyone to know what it is actually meaning. Please see the perlglossary(1) man page.Runic
This is about dative objects in English: indirect object: In English grammar, a short noun phrase between a verb and its direct object indicating the beneficiary or recipient of the action. In Perl, print STDOUT "$foo\n"; can be understood as “verb indirect-object object” where STDOUT is the recipient of the print action, and $foo is the object being printed. Similarly, when invoking a method, you might place the invocant in the dative slot between the method and its arguments: $gollum = new Pathetic::Creature "Sméagol"; give $gollum "Fisssssh!"; give $gollum "Precious!";Runic
indirect object slot: The syntactic position falling between a method call and its arguments when using the indirect object invocation syntax. (The slot is distinguished by the absence of a comma between it and the next argument.) STDERR is in the indirect object slot here: print STDERR "Awake! Awake! Fear, Fire, Foes! Awake!\n";Runic
@Runic tnx for pointing out parallels with natural language, and feel free to improve the answer. Personally I'm not a fan of indirect syntax as perl OO alone seems like unfortunate historic artefact.Racquelracquet
I’m certainly not going to go out of my way to defend dative-style (positionally based) method invocation in Perl. I do have a problem with the nomenclature, though, because it runs counter to traditional naming of these matters.Runic

© 2022 - 2024 — McMap. All rights reserved.