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

I have the following to output the contents of a $func which contains a code reference:

my $deparse = B::Deparse->new; print dumper $deparse->coderef2text($func);

It spits out:

$VAR1 = '{ package Spin::Command::spin; run(q[export MYVAR=\'(R)?ex\']); run(\'uname -a\'); run(q[echo \'Running: \' $MYVAR]); run(\'id\'); }';

I'd like to change the package from Spin::Command::spin to something else. Is this possible?

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Possible to change package of code reference?
by haukex (Archbishop) on Oct 21, 2018 at 18:43 UTC

    I'd be willing to bet this is an XY Problem... could you explain why you need this, preferably with an SSCCE?

    If you want the code that $func points to to call a different run, you can do the following (locally redefine &Spin::Command::spin::run), although this is kinda hacky too <update> because it just changes the call to run, it doesn't affect anything else in $func, like package variables or other function calls. </update> The "real" solution would be to modify $func at its origin.

    use warnings; use strict; { package Spin::Command::spin; sub run { print "Bar<<".shift.">>\n"; } sub func { run("Foo:".shift); } } my $func = \&Spin::Command::spin::func; use B::Deparse; my $deparse = B::Deparse->new; print "<<",$deparse->coderef2text($func),">>\n"; sub other_run { print "Quz[[".shift."]]\n"; } my $func_other_run = sub { no warnings 'redefine'; local *Spin::Command::spin::run = \&main::other_run; $func->(@_); }; $func->("Hello"); # prints "Bar<<Foo:Hello>>" $func_other_run->("World"); # prints "Quz[[Foo:World]]"

    Update 2: Of course you can also just clobber the original func:

    { no warnings 'redefine'; *Spin::Command::spin::func = $func_other_run; }

    WebPerl Live Demo

      An SSCCE would entail explaining a kind of dumb thing I'm doing in the first place. Perhaps I'll save that for another post. I was able to accomplish what I needed to by noodling out Lanx's suggestion. Cool job on the Live Demo, btw.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        I was able to accomplish what I needed to by noodling out Lanx's suggestion.

        Ok, but note LanX's warning, which I agree with - B::Deparse is more of a debugging tool, and does not guarantee to be able to round-trip Perl code, so your solution will be quite brittle. Using a regex to replace the package name will blow up on something as simple as:

        package Spin::Command::spin; our $foo = "Bar"; sub func { print "Foo:$foo"; }

        Because the deparse of func is:

        { package Spin::Command::spin; print "Foo:$foo"; }

        If, as you seem to be saying here, you really just want to replace run and don't need other replacements, then my suggestion, even though it's still kind of a hack, is much more robust (relative to it being a hack, at least).

Re: Possible to change package of code reference?
by LanX (Saint) on Oct 21, 2018 at 18:18 UTC
    Re eval sub body without the package and assign to $func.

    But I don't understand why you need to do it, there is no package var in the shown code.

    (Please don't use dumper for printing the text)

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      The function was generated by a config file with Perl code in it. The "run" commands in the subroutine are getting run by an object in the "Spin::Command::spin" class. But I want the commands to run under a method in a different class.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        I told you how, regex the new package into the text and eval it.

        $func= eval qq( sub {$newtext} );

        Another possibility is to temporarily patch &run before executing $func.

        local *Spin::Command::spin::run = sub { "new code" }

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice