6

Considering this code:

#!/usr/bin/perl
package p1;
use Data::Dumper;
sub dump_it {
    print Dumper @_
}

package p2;
local *foo = *p1::dump_it;
foo('this works');

#local *foo = *p1::dump_it("but this doesn't");
#foo;

I kind of understand, why passing a parameter here can't work, however I think it would be quite practical in some situations. Is there a way to make something similar to the commented-out part function? I would not want to wrap the dump_it in another sub, but directly use a reference.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Jan
  • 6,532
  • 9
  • 37
  • 48
  • Related: [Hook::LexWrap](https://metacpan.org/pod/Hook::LexWrap) – ikegami Aug 31 '22 at 17:56
  • Not that it matters that much, but what you are doing isn't currying where you are reducing the arity of a function by fixing one of its arguments. You are essentially reinventing Exporter, so reading that code might be of interest to you. https://stackoverflow.com/q/36314/2766176 – brian d foy Sep 01 '22 at 06:56

2 Answers2

7

You can wrap another sub around it, but use goto to prevent it from creating another stack:

sub foo {
    @_ = 'use goto';
    goto &p1::dump_it
}
choroba
  • 231,213
  • 25
  • 204
  • 289
  • Note that using `goto` is not just less readable; it's slower. – ikegami Aug 31 '22 at 17:43
  • @ikegami: Benchmark? – choroba Aug 31 '22 at 18:04
  • `use Benchmarks qw( cmpthese ); sub baz { } sub foo { @_="a"; goto &baz } sub bar { baz( "a" ) } cmpthese( -3, { foo => \&foo, bar => \&bar } );` The relative difference is large (almost twice as fast), but the absolute difference is small (70 ns for me) – ikegami Aug 31 '22 at 18:07
7

You need to create a sub.

sub foo {
   p1::dump_it( "wrapped", @_ );
}

But it can be an anonymous one.

local *foo = sub {
   p1::dump_it( "wrapped", @_ );
};

Maybe this is more your style:

sub prepend_args {
   my $sub = shift;
   my @to_prepend = @_;
   return sub { $sub->( @to_prepend, @_ ) };
}

local *foo = prepend_args( \&bar, "abc" );
foo( "def" );  # Same as `bar( "abc", "def" );`

See also Hook::LexWrap.


Since you mentioned currying,

# Needs to be extended to support more than two arguments.
sub curry {
   my $sub = shift;
   return sub {
      my $arg1 = shift;
      return sub {
         my $arg2 = shift;
         return $sub->( $arg1, $arg2 );
      };
   };
}

local *foo = curry( \&bar );
foo( "abc" )->( "def" );  # Same as `bar( "abc", "def" );`
ikegami
  • 367,544
  • 15
  • 269
  • 518