6

I'm making a package, where I have to get a symbol's value by its name in a sub, while the symbol is defined outside the sub.

Here is the simplified code, it works as expected:

#! /usr/bin/env perl6 

sub dump_value($symbol) {
    say ::("$symbol")
}

# usage:
my $x = 10;
dump_value('$x');

# expected output: 10
# actual output: 10

Then I put the 'dump_value' in a standalone file as below:

# somelib.pm6
unit module somelib;

sub dump_value($symbol) is export {
    say ::("$symbol")
}
# client.pl6
#! /usr/bin/env perl6

use lib ".";
use somelib;

my $x = 10;

dump_value('$x');

The compiler complained:

No such symbol '$x'
  in sub dump_value at xxx/somelib.pm6 (somelib) line 3
  in block <unit> at ./client.pl6 line 8

Following are some experiments. None of them succeeded.

say ::("MY::$symbol")

say ::("OUR::$symbol")

say ::("OUTER::$symbol")

say ::("CLIENT::$symbol")
...

So how to fix the code?

UPDATE:

Thank you! CALLERS::($symbol) solved my original problem. But in a bit more complex situation, the complier complained again:

# somelib.pm6
unit module somelib;

sub dump_value(@symbols) is export {
    # output: 6
    say CALLERS::('$x');

    # error: No such symbol 'CALLERS::$x'    
    say @symbols.map({ CALLERS::($^id) } ) 
}

# client.pl6
#! /usr/bin/env perl6

use lib ".";
use somelib;

my $x = 6;
my $y = 8;

dump_value(<$x $y>);

UPDATE AGAIN:

use OUTER::CALLERS::($^id).

UPDATE AGAIN AND AGAIN:

After I put the 'dump_value' in another sub, it didn't work any more!

# somelib.pm6
unit module somelib;

sub dump_value(@symbols) is export {
    say @symbols.map({ OUTER::CALLERS::($^id) } )
}

sub wrapped_dump_value(@symbols) is export {
    dump_value(@symbols)
}
#! /usr/bin/env perl6

use lib ".";
use somelib;

my $x = 6;
my $y = 8;

# ouput: (6 8)
dump_value(<$x $y>);

# error: No such symbol 'OUTER::CALLERS::$x'
wrapped_dump_value(<$x $y>);
Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
lovetomato
  • 903
  • 5
  • 11

2 Answers2

6

According to the documentation:

An initial :: doesn't imply global. Here as part of the interpolation syntax it doesn't even imply package. After the interpolation of the ::() component, the indirect name is looked up exactly as if it had been there in the original source code, with priority given first to leading pseudo-package names, then to names in the lexical scope (searching scopes outwards, ending at CORE).

So when you write say ::("$symbol") in dump_value() in the somelib package, it will first lookup $symbol in the current scope, which has value '$x' then try to look up $x (also in the current scope), but the variable $x is defined in the caller's lexical scope, so you get the No such symbol '$x' error.

You can refer to the caller's lexical symbol given by the value of $symbol using either:

CALLER::MY::($symbol);  # lexical symbols from the immediate caller's lexical scope

or

 CALLERS::($symbol); # Dynamic symbols in any caller's lexical scope

see the package documentation page.

raiph
  • 31,607
  • 3
  • 62
  • 111
Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
  • I am sorry to bother you again, but please see the update at the bottom. – lovetomato Feb 07 '19 at 02:56
  • No problem! Try `OUTER::CALLERS::CALLERS::($^id)` – Håkon Hægland Feb 07 '19 at 07:21
  • The [doc](https://docs.perl6.org/language/packages#Interpolating_into_names) said: `CALLERS` means `Dynamic symbols in **any** caller's lexical scope`. So why does it need two CALLERS? I have thought one was enough. – lovetomato Feb 07 '19 at 08:34
  • I am not sure, maybe a bug or something that I missed. Maybe some of the experts here can answer this? I agree that this is unclear and I will look into it also.. – Håkon Hægland Feb 07 '19 at 08:38
0

Couple of things:

use lib ".";
use somelib;
our $x = 10;     # You need to export the value into the global scope
dump_value('$x');

Then, use the global scope:

unit module somelib;

sub dump_value($symbol) is export {
    say GLOBAL::("$symbol")
}
jjmerelo
  • 22,578
  • 8
  • 40
  • 86