calling subroutines in perl plack return nothing

161 Views Asked by At

I am new to perl plack/psgi. I want to access a subroutine within the perl plack/psgi loop, but it looks like if the subroutine is not being executed. Every parent variable like $number should being passed automatically like when writing a regular perl script. Do I miss something here, is that possible?

..
my $app = sub {

  my $number = 10;

  &count_number;
  sub count_number {
    $number +=10;
  }


  return ['200',[  'Content-Type' => 'application/json' ],
  [ "{\"number\":$number} ]];     

}
.. 

10 is being returned instead of 20 :(

2

There are 2 best solutions below

0
Borodin On BEST ANSWER

If I fix the quotes on the string in the return statement (you are missing a closing double-quote) then I get the warning

Variable "$number" is not available at source_file.pl line 7.

The reason is that lexical values $app and $number are defined at run time, whereas the subroutine count_number is defined much earlier during compilation

The solution is to defer definition of count_number to run time by making it an anonymous subroutine instead. The call $count_number->() also needs to be moved to after the definition

my $app = sub {

    my $number = 10;

    my $count_number = sub {
        $number +=10;
    };

    $count_number->();

    return [
        '200',
        [ 'Content-Type' => 'application/json' ],
        [ "{\"number\":$number}" ]
    ];     
};


use Data::Dumper;
print Dumper $app->();

output

$VAR1 = [
            '200',
            [
                'Content-Type',
                'application/json'
            ],
            [
                '{"number":20}'
            ]
        ];

There is a related warning

Variable "$number" will not stay shared

with a similar solution. You can read about both in perldoc perldiag. The messages are listed and described in alphabetical order

1
amon On

The my operator has two effects:

  • at compile time, it introduces a scalar variable.
  • at run time, it creates a fresh scalar object for that variable.

Essentially, these two scalars are different variables, although they have the same name.

The sub name { ... } operator only has a compile time effect. It assigns the subroutine at compile time to the given name. So when the sub is compiled, it sees the original compile-time variable, not the run-time variable which is created much later.

Therefore, you should not nest named subs. In fact, if you use warnings you get a warning about this: “Variable "$number" will not stay shared”.

You have two options:

  • You can use a closure that sees the runtime variable. This uses anonymous subroutines:

    ...
    my $number = 10;
    my $count_number = sub {
      $number += 10;
    };
    
    $count_number->();
    
    ...
    
  • Or, you pass the values as explicit parameters to a separate subroutine. Yes, that does complicate things a bit, but it also keeps separate things separate. Clear data flow is a characteristic of good design.