Can anybody please explain (my $self = shift) in Perl
Asked Answered
E

3

31

I'm having a really hard time understanding the intersection of OO Perl and my $self = shift; The documentation on these individual elements is great, but none of them that I've found touch on how they work together.

I've been using Moose to make modules with attributes, and of course, it's useful to reference a module's attribute within said module. I've been told over and over again to use my $self = shift; within a subroutine to assign the module's attributes to that variable. This makes sense and works, but when I'm also passing arguments to the subroutine, this process clearly takes the first element of the @ARGV array and assigns it to $self as well.

Can someone offer an explanation of how I can use shift to gain internal access to a module's attributes, while also passing in arguments in the @ARGV array?

Excommunicatory answered 3/10, 2013 at 14:29 Comment(0)
G
66

First off, a subroutine isn't passed the @ARGV array. Rather all the parameters passed to a subroutine are flattened into a single list represented by @_ inside the subroutine. The @ARGV array is available at the top-level of your script, containing the command line arguments passed to you script.

Now, in Perl, when you call a method on an object, the object is implicitly passed as a parameter to the method.

If you ignore inheritance,

 $obj->doCoolStuff($a, $b);

is equivalent to

 doCoolStuff($obj, $a, $b);

Which means the contents of @_ in the method doCoolStuff would be: @_ = ($obj, $a, $b);

Now, the shift builtin function, without any parameters, shifts an element out of the default array variable @_. In this case, that would be $obj.

So when you do $self = shift, you are effectively saying $self = $obj.

I also hope this explains how to pass other parameters to a method via the -> notation. Continuing the example I've stated above, this would be like:

sub doCoolStuff {
  # Remember @_ = ($obj, $a, $b)
  my $self = shift;
  my ($a, $b) = @_;

Additionally, while Moose is a great object layer for Perl, it doesn't take away from the requirement that you need to initialize the $self yourself in each method. Always remember this. While language like C++ and Java initialize the object reference this implicitly, in Perl you need to do it explicitly for every method you write.

Giraffe answered 3/10, 2013 at 14:33 Comment(8)
shift defaults to @_ if called from inside a subroutine, or @ARGV if called from top-level code.Mouseear
@Mouseear Right you are. Will mention that in the answer.Giraffe
Awesome. That's so helpful. I did run into an issue where my $self = shift seemed to be setting itself to the first argument I actually passed into the subroutine. Ie, the first element in @_ wasn't the object, but was infact the first argument I provided. Any high level reason this would occur? (I worked around this problem and don't have the actual code anymore)Excommunicatory
When you are unsure as to what is contained in a Perl var output the content using the Data::Dumper module.Penick
@AlexHHadik I can imagine only one situation where such a thing would happen. When instead of calling your method via the object like this: $obj->method($arg1, $arg2) you miss the object and call the method directly method($arg1, $arg2). That is perfectly legal in Perl, since a method call is no different from a normal function call. But since you didn't invoke it through an object using the -> operator, the $obj parameter isn't passed to the method in the arg list.Giraffe
Ahh that makes much more sense @Giraffe - is there a way to call a function in that manner, and reference the object?Excommunicatory
Very nice explanation, concise but readable. I've been scripting in Perl for years but have never used the OO features until now ... this (among other things) had me scratching my head.Senghor
@Alex H Hadik, Re "is there a way to call a function in that manner, and reference the object?", No, because it can't possibly guess which object should be used if you don't specify it. However, it is possible to convert a method call to a sub call as follows: my $sub = $o->can('doCoolStuff'); $sub->($o, $arg1, $arg2);.Venetic
V
8

In top level-code, shift() is short for shift(@ARGV). @ARGV contains the command-line arguments.

In a sub, shift() is short for shift(@_). @_ contains the sub's arguments.

So my $self = shift; is grabbing the sub's first argument. When calling a method, the invocant (what's left of the ->) is passed as the first parameter. In other words,

$o->method(@a)

is similar to

my $sub = $o->can('method');
$sub->($o, @a);

In that example, my $self = shift; will assign $o to $self.

Venetic answered 3/10, 2013 at 14:43 Comment(0)
S
3

If you call:

$myinstance->myMethod("my_parameter");  

is the same that doing:

myMethod($myinstance, "my_parameter");  

but if you do:

myMethod("my_parameter");  

only "my_parameter" wil be passed.

THEN if inside myMethod always you do :

 $self = shift @_;  

$self will be the object reference when myMethod id called from an object context
but will be "my_parameter" when called from another method inside on a procedural way.
Be aware of this;

Softball answered 3/3, 2014 at 0:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.