Search code examples
perloopreturn-valuedereference

How do you dereference return values?


I keep running into problems with dereferencing, especially when returning values from functions.

The issue seems to be that whenever you return anything other than a scalar you are actually returning that object by reference - which is fine by me - but say when we pass these references to other functions and we need access to their guts again how do we do that correctly?

I keep running into errors like: "expecting even number of params got reference" or something to the effect.

Is there a general rule of thumb that I can use to simplify this whole process? I almost wish I didnt have to worry about dereferencing!

Here's an example of something I tried to do earlier today, and ran into all kinds of dereferencing problems that I spent a couple of hours trying to bash my way through -- so after reading, trying and failing, I'm here to ask you for the lowdown.

Person object

Person
 has name [Str]
 has madeby [Str]
 has height [Num]
 has id [Num]

Various ways of making a Person object

sub person_maker{
 my($this,%options) = @_;
 my $person = Person->new(%options);
   return $person;
}

sub make_person_named_james{
 my($this,$options) = @_;
 my $default = { name => 'James', madeby => 'name' };
   $options = ($options,$defaults); #merge default and options hash
   return($this->person_maker($options));
}

sub make_person_from_id{
 my($this,$id) = @_;
 my $default = { name => 'nameless person', madeby => 'id' };
   $default = ($default,{ id => $id });
   return($this->person_maker($default);
}

sub make_person_from_person{
 my($this,$person) = @_;
 my $person_options = {
   name => $person->name().'_twin',
   height => $person->height(),
   id => $person->id() + 1,
   madeby => 'person'
 };
 return($this->person_make($person_options));
}
  • Func returns hash object => it actually returns as a hash reference
  • Func returns hash reference => it actually returns as a scalar
  • Func returns array object => it actually returns an array reference
  • Func returns array reference => it actually returns a scalar ?
  • Func returns a scalar => it actually returns the value of the scalar?

Correct me if I've understood any of that wrong.

Then another issue for me is at the point of consuming the arguments of a function..

Depending on what I return back these bad boys are all going to behave differently!

    sub myfunc{
      my($self,$args) = @_ # list of arguments (what if the args are mixed?)
  OR
      my($self,$args) = $_ # scalar (this wont work here $args will by undef
  OR
      my $self = shift; # pop the first arg
      my $args = shift; # pop the second arg
  OR
      my $self = $_[0] 
      my $args = $_[1]

Plus! There are way too many documents out there, many of them outdated, so it's hard to figure out exactly what the right or best thing to do is in these situations.

If someone has a magic chart that spells out when to use these different setups, and how to dereference given certain scenarios, blessed Hashes, Hash Ref, Scalar, etc etc. I would be eternally grateful as I've wasted hours trying to decipher this.


Solution

  • All references are scalar values

    De-referencing a reference requires that you know the type of the reference. The type of a reference can be found by using the ref function.

    my $array_ref = [];
    print ref $array_ref;  # prints: ARRAY
    

    De-referencing different types

    • Scalar reference: $$scalar_ref

    • Array reference: @$array_ref

    • Hash reference: %$hash_ref

    @_

    @_ contains aliases of the parametres. Modifying @_ results in the modification of the original values. Always make a copy of the parametres as soon as possible and work on those copies; which you can safely modify without the original values changing.

    Arguments and return values

    In Perl, all function call arguments are flattened (collapsed) to a list (thus losing their identities) and so are the return values. From a function's perspective, it is able to accept only and a single list of values and can return only a single list of values.

    Example scenarios:

    Scalar argument to a function:

    sub foo {
        my $arg = shift;
    }
    

    The same applies to all references; references are scalar values.

    Array argument to a function:

    sub foo {
        my @args = @_;
    }
    
    foo( 'bar', 'baz' );
    foo( ( 'bar', 'baz' ), ( 'qux', 'spam' ) );  # In no way can the foo subroutine identify that the original arguments were two lists (same with arrays).
    

    Hash argument to a function:

    sub foo {
        my %arg = @_;
    }
    
    foo( 'bar' => 'baz' );
    foo( ( 'bar' => 'baz' ), ( 'qux' => 'spam' ) );  # In no way can the foo subroutine identify that the original arguments were two hashes.
    

    Always pass references when multiple lists (arrays) or hashes are involved. This way, you can identify the lists individually.

    $_ and @_ are different

    Quoting your code (which uses $_ incorrectly):

    my($self,$args) = $_ # scalar (this wont work here $args will by undef
    

    $_ is called the default variable and is used in a number of situations where no explicit variable is stated. @_ on the other hand is only used for holding array parametres (aliases) inside a function. To refer to each element, we can use: $_[0], $_[1], et cetera.

    References

    • You can read more about predefined variables in Perl from perldoc perlvar. I always use perldoc -v '$special_variable' from my terminal. If you use Windows, the single quotes have to be replaced with double quotes: perldoc -v "$special_variable".

    • Perl subroutines: perldoc perlsub

    • Perl references and nested data structures: perldoc perlref