1

I can't seem to get to figure out how to pass hashrefs to a module correctly.

#module helper.pm    

my %helpers;

sub import {
    shift @_; #returns "helper"
    %SOME_CONFIG = %{shift @_};
    foreach my $hashref (%{@_}){
        $helpers{$hashref->{key}} = $hashref->{value};
    }
}

#main

use helper(
    \%SOME_CONFIG,
    "first_helper" => (
        sub {
            return do_something();
        },
        sub {
            return do_something_else();
        }
    ),
    "second_helper" => (
        sub {
            return something_else();
        }
    )
);

I'm not sure how to convert the array to a hash correctly. I thought it work because the anonymous function are passed as references. However it still indexes over @_ as if it is an array and the keys become the indexes of @_.

2 Answers 2

2

Lowercase module names are reserved by Perl, and the convention is to use them for pragmas. That's two misses, so let's rename the module to Helper. (Don't forget to change the file name to match!)


The arguments are

  • "Helper"
  • \%SOME_CONFIG
  • "first_helper"
  • sub { 1 }
  • sub { 2 }
  • "second_helper"
  • sub { 3 }

You probably meant to pass the sub refs in arrays, but you used parens. Parens just change precedence. Instead, you should have used the following:

use Helper (
    \%SOME_CONFIG,
    "first_helper" => [
        sub {
            return do_something();
        },
        sub {
            return do_something_else();
        }
    ],
    "second_helper" => [
        sub {
            return something_else();
        }
    ],
);

Now, the arguments are

  • "Helper"
  • \%SOME_CONFIG
  • "first_helper"
  • [ sub { 1 }, sub { 2 } ]
  • "second_helper"
  • [ sub { 3 } ]

There's no hash in there other than \%SOME_CONFIG, but you have a hash dereference. That's no good. We could build a hash in the caller, but there's no need to make that any longer since we can handle the data as-is. Solutions:

sub import {
    my $class = shift;
    my $config = shift;
    my %new_helpers = @_;
    for my $id (keys(%new_helpers)) {
        $helpers{$id} = $new_helpers{$id};
    }
}

or

sub import {
    my $class = shift;
    my $config = shift;
    %helpers = ( %helpers, @_ );
}

or

sub import {
    my $class = shift;
    my $config = shift;
    while (@_) {
       my $id = shift;
       $helpers{$id} = shift;
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

If your module is not a pragma, i.e. it doesn't change how the code is parsed, use a capitalized name.

Values of hashes mush be scalars, so if you want to assign several subroutines to a key, you must use an array reference.

#! /usr/bin/perl
use warnings;
use strict;

{
    package Helper;
    my %helpers;

    sub import {
        my $class = shift;
        my %SOME_CONFIG = %{ shift; };
        for my $hashref (@_){
            @helpers{keys %$hashref} = values %$hashref;
        }
        use Data::Dumper;
        $Data::Dumper::Deparse = 1;  # To show the code, as well.
        warn Dumper \%SOME_CONFIG, \%helpers;
    }
}


my %SOME_CONFIG = ( field => 'and its value' );

sub do_something {
    warn "Doing something";
}

sub do_something_else {
    warn "Doing something else";
}

Helper->import(
    \%SOME_CONFIG,
    { first_helpers => [
        \&do_something,
        \&do_something_else,
    ] },
    { second_helpers => [
        sub {
            warn "Inside the 2nd helper";
        },
    ] },
);

If you put Helper into a file of its own, Helper.pm, you can then just

use Helper (
    \%SOME_CONFIG,
    ....

BUT, use clauses are processed at compile time, when %SOME_CONFIG still hasn't been populated, so you need to define it in a BEGIN block. You still need to declare it outside the block, though, to make it accessible in the use clause:

my %SOME_CONFIG;
BEGIN {
    %SOME_CONFIG = ( field => 'and its value' );
}

Simplified as ikegami suggested:

In Helper.pm, just do

%helpers = @_;

instead of the for my $hashref. In the main program, use

use Helper(
    \%SOME_CONFIG,
    first_helpers => [
        \&do_something,
        \&do_something_else,
    ],
    second_helpers => [
        sub {
            warn "Inside the 2nd helper";
        },
    ],
);

1 Comment

{ first_helpers => ..., }, { second_helpers => ... } should be { first_helpers => ..., second_helpers => ... } (with the appropriate change to import)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.