Perl: What's the proper way to pass a hash ref plus additional keys/values as one hash ref?

262 views Asked by At

The purpose is to only ammend the hashref argument being passed to the function, but not modify the original hashref.

{
    my $hr = { foo => q{bunnyfoofoo},
               bar => q{barbiebarbar} };

    foo( { %$hr, baz => q{bazkethound} } );
}

sub foo {
   my $args = shift // {};
}

I think the above would work, but am not sure there is a better way (other than modifying the orginal ref before passing).

2

There are 2 answers

13
ikegami On BEST ANSWER

Your code is as simple as it can be. You need to pass a hash and you don't want to modify the existing hash, so you need to create a new hash. That's what { } does.

The underlying problem is foo takes a hash ref instead of a key-value list.

sub foo {
   my %args = @_;
   ...
}

You can still do everything you did before:

foo( foo => 'bunnyfoofoo', bar => 'barbiebarbar' );

my $hash = {
   foo => 'bunnyfoofoo',
   bar => 'barbiebarbar',
};
foo( %$hash, baz => 'bazkethound' );

There are two advantages.

  1. You don't have to use { } all over the place.

  2. You can modify the argument safely.

    This is buggy:

    sub f {
       my $args = $_[0] // {};
       my $foo = delete($args->{foo});
       my $bar = delete($args->{bar});
       croak("Unrecognized args " . join(', ', keys(%$args)))
          if %$args;
       ...
    }
    

    This is ok:

    sub f {
       my %args = @_;
       my $foo = delete($args{foo});
       my $bar = delete($args{bar});
       croak("Unrecognized args " . join(', ', keys(%args)))
          if %args;
       ...
    }
    
0
Oleg V. Volkov On

Whatever the syntax you choose, it will still boil down to shallow or deep (if you need to modify inside function) copy of original.

If your function and everything inside it is guaranteed not to modify hash, then assigning this key before call and restoring previous value or deleting it after the call could be enough as well.