Search code examples
perlsubroutine-prototypes

How should I modify the prototype to allow construction of a hash after the coderef?


This is what I have:

use 5.14.0;
use strict;
use warnings;

sub my_func(&$) {
    my $coderef = shift;
    my %attribs = @_;
}

This is what I'd like to achieve:

my_func {
    print 1;
} first_attrib => "1",second_attrib => "2";

However, I receive the error Too many arguments for main::my_func at x.pl line 12, near ""2";". How should I modify the prototype, so that the parameters after the coderef will be transformed into a hash?


Solution

  • If you change sub my_func(&$) to sub my_func(&%) your code will work.

    The problem is that first_attrib => "1",second_attrib => "2" isn't a hash ref, but a list. And as friedo pointed out a list can be assigned to a hash, though a list with an odd number of elements might produce unwanted results and will produce a warning with use warnings.

    Alternatively you can change your code to

    sub my_func(&$) {
        my $coderef = shift;
        my ($attribs) = @_;
    }
    
    my_func {
        print 1;
    } {first_attrib => "1",second_attrib => "2"};
    

    to achieve what you seem to want.

    The reason why you must wrap $attribs in parens is that assigning an array to a scalar returns just the number of elements in the array. At this point @_ is this array:

    ({first_attrib => "1",second_attrib => "2"})
    

    with a hash ref as a single element.

    ($attribs) = @_;
    

    tells perl to create an anonymous array with the scalar $attribs as its first element, and assign elements from @_ to the anonymous array, element by element, thus making $attribs point at the hash ref in @_.