jignasu has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,
I am a little confused about the way Perl treats hash references being passed into functions. Here is a test case:
#!/usr/local/bin/perl -w use strict "vars"; use strict "subs"; sub inter(\%) { printf "$_[0]\n"; printf "@_\n"; printf "$_[0]->{a}\n"; printf "$_[0]{a}\n"; } printf "ONE\n"; my %c = (a=>"a",b=>"b"); inter(%c); printf "TWO\n"; my $ref = "inter"; $ref->(%c); printf "THREE\n"; $ref->(\%c);
output:
ONE HASH(0x622430) HASH(0x622430) a a TWO a a a b b Use of uninitialized value in concatenation (.) or string at /home/smk +/prog/pl/interesting.pl line 10. Use of uninitialized value in concatenation (.) or string at /home/smk +/prog/pl/interesting.pl line 11. THREE HASH(0x622430) HASH(0x622430) a a
Questions:
1. The function definition specifies that the argument is a reference to a hash. Then why does block ONE in the code above need a hash to be passed to inter? If I change the line
inter(%c)
to
inter(\%c)
then I get a compilation error as follows:
Type of arg 1 to main::inter must be hash (not reference constructor) +at /home/smk/prog/pl/interesting.pl line 18, near "%c)" Execution of /home/smk/prog/pl/interesting.pl aborted due to compilati +on errors.
Why is this?

2. Why does the argument have to be a reference again if the function itself is being called by reference? (block THREE)

3. In block TWO, it looks like the function is treating the argument as an array.

4. Finally, inside the function, in the final two printfs, I am using different notations, one if $_[0] was a hash, and another if it was a reference. Both work similarly. How can this be explained?

Thanks a lot!

Replies are listed 'Best First'.
Re: references and functions
by Errto (Vicar) on Aug 24, 2007 at 00:13 UTC

    1. The way to think of the \ in the prototype is "your parameter must be a real ..." So in this case, it has to be a real hash. Meaning, if you're calling this function, the first character of your argument had better be %. It can be %hash or %{ some_hash_ref_expr } or even %{'foo'} if you're not using strict, but it has to start with %. Then inside the body of your subroutine, what you get is a hash reference.

    2. Prototypes only work when you call the function directly by name, as a list operator. They don't work when you call the subroutine by reference. So if you want to pass in a hash reference, you have to do it explicitly. I don't know the exact reason for this, but it's been an issue since prototypes appeared in Perl.

    3. Same reason as #2. When you call the subroutine as a reference, the prototype doesn't apply so your arguments are no longer a hash per se, but a list of values that happen to have come from a hash.

    4. $_[0]->{a} and $_[0]{a} are exactly equivalent. The second is just a shorthand in which the dereferencing arrow is inferred rather than explicitly written out. ${$_[0]}{a} would also be equivalent, but no one trying to write clear code would choose that on purpose. See perlref, note 3 under "Using References".

    For further explanations on points 1-3 see perlsub under Prototypes.

Re: references and functions
by FunkyMonk (Bishop) on Aug 23, 2007 at 23:46 UTC
    The short answer to your question is: don't use subroutine prototypes. In Perl they don't act like they do in C. They only exist so that you can use make your subs work like Perl's builtins (eg grep { CONDITION } LIST).

    If you want to make sure that your subs are passed the correct type of argument, check your arguments in the sub. Generally, though, the assumption should be made that the caller is doing the right thing, and leave perl to catch the errors.

    The really short answer is: If you need to ask, you don't want (or need) them.

    updated