Importing an hash in a lexical scope

S

Stefano Sabatini

Hi perl guys,

I would like to import an hash in a lexycal scope, so to be able
to access the values of the hash as they were lexicals variables
defined in the current lexycal scope.

For example I would like to define somewhere an hash like:
my %hash = (one => 1, two => 2, three => 3, four => 4);

and then be able to access the variables $one, $two, $three after
importing them:
{
# import the hash
...
print "One is $one, two is $two, three is $three and so on";
}

I successfully achieved to import an hash into the current package using
this incantation:

foreach (keys %hash) {
*$_ = \$hash{$_};
}

I also tried this:

{
my $keys_str;
foreach (keys %hash) { $keys_str.= "\$$_, " };

# here it evals the code in the evalled code lexycal environment, which is
# (unfortunately) not the current lexycal environment, so the
# lexycal binding is immediately discarded

eval "my ($keys_str)";

no strict;
# this cause the creation of global (package) variables
# and don't warn 'cause of the no strict pragma
foreach (keys %hash) {
eval "\$$_ = $hash{$_};";
}
}

but after some debugging and thinkering I realized it can't work (as
explained in the comments above).

So I'm thinking maybe it's not possible at all to achieve my goal, or
maybe I can't simply see how.

Any help or suggestion will be highly appreciated.

Many cheers
 
U

Uri Guttman

SS> I would like to import an hash in a lexycal scope, so to be able
SS> to access the values of the hash as they were lexicals variables
SS> defined in the current lexycal scope.

SS> For example I would like to define somewhere an hash like:
SS> my %hash = (one => 1, two => 2, three => 3, four => 4);

SS> and then be able to access the variables $one, $two, $three after
SS> importing them:
SS> {
SS> # import the hash
SS> ...
SS> print "One is $one, two is $two, three is $three and so on";
SS> }

that is a very foolish idea in several ways. first, you can't import
into lexical scope, only into the symbol table. second, the whole point
of hashes is not to have individual variables for each value when they
are related. what you are asking for is symbolic (as i said, lexical
won't even work) references which are a very bad idea.

SS> I successfully achieved to import an hash into the current package using
SS> this incantation:

SS> foreach (keys %hash) {
SS> *$_ = \$hash{$_};
SS> }

was use strict enabled? won't work then.

SS> I also tried this:

SS> {
SS> my $keys_str;
SS> foreach (keys %hash) { $keys_str.= "\$$_, " };

SS> # here it evals the code in the evalled code lexycal environment, which is
SS> # (unfortunately) not the current lexycal environment, so the
SS> # lexycal binding is immediately discarded

SS> eval "my ($keys_str)";

nasty.

SS> no strict;
SS> # this cause the creation of global (package) variables
SS> # and don't warn 'cause of the no strict pragma
SS> foreach (keys %hash) {
SS> eval "\$$_ = $hash{$_};";
SS> }
SS> }

again nasty.

SS> but after some debugging and thinkering I realized it can't work (as
SS> explained in the comments above).

why do you think you need to do this? will it make your code much
cleaner or easier? since you MUST know the names of the hash keys in
advance since you want to refer to them by name (in whatever scope), why
would a general import help? all you save would be typing the name of
the hash.

SS> So I'm thinking maybe it's not possible at all to achieve my goal, or
SS> maybe I can't simply see how.

you can't in a lexical scope. it is dumb in the symbol table.

SS> Any help or suggestion will be highly appreciated.

don't do it at all. use the hash as it is. you won't gain much at all
doing what you want. you may think you do but there are other
ways. since you MUST know the names you will be using, why not just do
this:

my $first = $hash{first} ;

and so on. do that in the tightest scope just before where you use
$first. and if you only use $first one time then don't even do that,
just use the hash.

uri
 
S

Stefano Sabatini

Hi Guri, and thanks for your reply.

SS> For example I would like to define somewhere an hash like:
SS> my %hash = (one => 1, two => 2, three => 3, four => 4);

SS> and then be able to access the variables $one, $two, $three after
SS> importing them:
SS> {
SS> # import the hash
SS> ...
SS> print "One is $one, two is $two, three is $three and so on";
SS> }

that is a very foolish idea in several ways. first, you can't import
into lexical scope, only into the symbol table. second, the whole point
of hashes is not to have individual variables for each value when they
are related. what you are asking for is symbolic (as i said, lexical
won't even work) references which are a very bad idea.

SS> I successfully achieved to import an hash into the current package using
SS> this incantation:

SS> foreach (keys %hash) {
SS> *$_ = \$hash{$_};
SS> }

was use strict enabled? won't work then.

well, I was using no strict there.
is there a better way to import an hash in the main package (without
disabling the strict pragma), or is this a bad practice to avoid?
SS> I also tried this:

SS> {
SS> my $keys_str;
SS> foreach (keys %hash) { $keys_str.= "\$$_, " };

SS> # here it evals the code in the evalled code lexycal environment, which is
SS> # (unfortunately) not the current lexycal environment, so the
SS> # lexycal binding is immediately discarded

SS> eval "my ($keys_str)";

nasty.

SS> no strict;
SS> # this cause the creation of global (package) variables
SS> # and don't warn 'cause of the no strict pragma
SS> foreach (keys %hash) {
SS> eval "\$$_ = $hash{$_};";
SS> }
SS> }

again nasty.

well, I agree, and indeed it was just an experiment, and I'd think twice
before to put it in production code.
SS> but after some debugging and thinkering I realized it can't work (as
SS> explained in the comments above).

why do you think you need to do this? will it make your code much
cleaner or easier? since you MUST know the names of the hash keys in
advance since you want to refer to them by name (in whatever scope), why
would a general import help? all you save would be typing the name of
the hash.

I have this problem. There is portion of user defined code (via a
code reference) loaded at run-time, and an hash containing options values
used in that code, which again is defined by the user at run-time.

I would like the user to use all the variables defined in the hash as
they were defined as regular variables in the code environment, rather
than hash entries.

For example:

my %options= (name => "John Doe",
programming_language_used => "Perl",
vice => "nastiness");


then I would like to access these values in the code in this way:

my $funref = {
print "My name is $name, I'm programming in $programming_language_used, ",
"my vice is $vice";
}

rather than like this:
my $funref = {
# $options is a reference to the options hash
my ($options) = @_;
print "My name is $options->{name}, ",
"I'm programming in $options->{programming_language_used}, ",
"my vice is $options->{vice}";
}

but, basing on what you said, I see it would be better (easier on the
programmer, *safer*) the latter, even if it is sligthly more prolisse.
SS> So I'm thinking maybe it's not possible at all to achieve my goal, or
SS> maybe I can't simply see how.

you can't in a lexical scope. it is dumb in the symbol table.

SS> Any help or suggestion will be highly appreciated.

don't do it at all. use the hash as it is. you won't gain much at all
doing what you want. you may think you do but there are other
ways. since you MUST know the names you will be using, why not just do
this:

my $first = $hash{first} ;

and so on. do that in the tightest scope just before where you use
$first. and if you only use $first one time then don't even do that,
just use the hash.

OK, and many thanks for your knowledgable help.

as I said I was experimenting, but it seems that the solution I was
contriving wasn't a good one.

Cheers
 
U

Uri Guttman

SS> well, I was using no strict there.

that is the whole problem. strict is meant to stop you from doing evil
things.

SS> is there a better way to import an hash in the main package (without
SS> disabling the strict pragma), or is this a bad practice to avoid?

you haven't explained your bigger picture. why do you need to import the
hash? can you pass it as an argument by reference? can you remotely
access it via a sub or method? there are other ways to share data than
just importing. you seem to be stuck on only import and i see no reason
from you why that is so critical.
SS> {
SS> my $keys_str;
SS> well, I agree, and indeed it was just an experiment, and I'd think twice
SS> before to put it in production code.

good! you learned something! :)

SS> I have this problem. There is portion of user defined code (via a
SS> code reference) loaded at run-time, and an hash containing options values
SS> used in that code, which again is defined by the user at run-time.

SS> I would like the user to use all the variables defined in the hash as
SS> they were defined as regular variables in the code environment, rather
SS> than hash entries.

that is bas as i have already explained. if they know the names of the
hash elements (not variables) they can use those directly. having them
automatically assigned to lexicals is not a win. let them do it in code
as i have shown you if they want that.

SS> print "My name is $name, I'm programming in
SS> $programming_language_used, ", "my vice is $vice";


SS> rather than like this:
SS> my $funref = {
SS> # $options is a reference to the options hash
SS> my ($options) = @_;
SS> print "My name is $options->{name}, ",
SS> "I'm programming in $options->{programming_language_used}, ",
SS> "my vice is $options->{vice}";
SS> }

so what is so bad about that? use a shorter name for $options if you
want. or use a templater (see Template::Simple on cpan for something
that will do that quickly and easily)

untested:

use Template::Simple ;

my $tmpl = Template::Simple->new() ;

my $in = <<INPUT ;
My name is [%name%],
I'm programming in [%programming_language_used%],
my vice is [%vice%]
INPUT

# put into $options the hash info as above (no code here for that)

print $tmpl->render( $options, $input ) ;

clean, fast, safe, no extra $options all around. $tmpl is reusable too.

SS> but, basing on what you said, I see it would be better (easier on the
SS> programmer, *safer*) the latter, even if it is sligthly more prolisse.

SS> So I'm thinking maybe it's not possible at all to achieve my goal, or
SS> maybe I can't simply see how.

your goal isn't worthy of being achieved as it is misguided. putting
lexicals into a remote scope would be dangerous even it could be
done. what if outside data set some other lexical that was used for some
external operation? a security hole a mile wide. php is famous for this
sort of thing (all cgi params automatically become variables, same nasty
concept).

as i have shown you here, you can use a templater. or have the users
assign their own lexicals. and there is nothing wrong with your basic
hash access as you know that works. why do you think your users need
such a minor extra little helper like your lexical idea? the other
solutions are almost as short and so much better in many ways.

uri
 
S

Stefano Sabatini

SS> well, I was using no strict there.

that is the whole problem. strict is meant to stop you from doing evil
things.

SS> is there a better way to import an hash in the main package (without
SS> disabling the strict pragma), or is this a bad practice to avoid?

you haven't explained your bigger picture. why do you need to import the
hash? can you pass it as an argument by reference? can you remotely
access it via a sub or method? there are other ways to share data than
just importing. you seem to be stuck on only import and i see no reason
from you why that is so critical.

There is no particular reason for wondering about to import an hash. I
only stepped on the problem, I considerd the basic hash access method
and I wondered if it was possible to use that other method, that
seemed to slightly simplify the user code. Maybe I should have asked
myself if that was a good thing to do, and as you say maybe it's not
(for the same reasons you explained).

But I thought that was an interesting problem, I meant good to stretch
my own knowledge and awareness of the language.

And if you look at the problem from another point of view maybe my
question doesn't appear so much dumb: it's possible to import some
symbols from a package (that is an hash) into the current package,
it's possible (although disabling strict) to import a regular hash in
the current package as I showed, so maybe it could be possible to
import an hash in a lexycal environment.
your goal isn't worthy of being achieved as it is misguided. putting
lexicals into a remote scope would be dangerous even it could be
done. what if outside data set some other lexical that was used for some
external operation? a security hole a mile wide. php is famous for this
sort of thing (all cgi params automatically become variables, same nasty
concept).
as i have shown you here, you can use a templater. or have the users
assign their own lexicals. and there is nothing wrong with your basic
hash access as you know that works. why do you think your users need
such a minor extra little helper like your lexical idea? the other
solutions are almost as short and so much better in many ways.

OK, indeed it's doesn't seems that bad, and I think I'll stick with
the basic hash access.

Many thanks.

Cheers
 
M

Mumia W.

you haven't explained your bigger picture. why do you need to import the
hash? [...]

There is no particular reason for wondering about to import an hash. I
only stepped on the problem, I considerd the basic hash access method
and I wondered if it was possible to use that other method, that
seemed to slightly simplify the user code. Maybe I should have asked
myself if that was a good thing to do, and as you say maybe it's not
(for the same reasons you explained).

But I thought that was an interesting problem, I meant good to stretch
my own knowledge and awareness of the language.
[...]

I probably have no business posting this since the thread is basically
over, but I just got so curious that I had to see if it was possible,
and it is:

#!/usr/bin/perl
use strict;
use warnings;
use English qw(-no_match_vars);

my %h = (USER => 'han',
EMAIL => '(e-mail address removed)',
PASSWORD => 'passWord');

$RS='';
while (my $custcode = <DATA>) {
my $code = join "",
map "my \$$_ = q{$h{$_}};\n", keys %h;
$code .= "\n$custcode\n";
# print $code;
eval $code;
if ($@) { die $@; }
}

__DATA__
local ($, , $\ ) = (' , ', "\n");
print "User Info:", $USER, $EMAIL, $PASSWORD;

print "My customer's email address is $EMAIL.\n";

use Digest::MD5 qw(md5_hex);
local $\ = "\n";
print "User ${USER}'s md5 password = ", md5_hex($PASSWORD);

__END__

In this program, each segment of customer code is separated by a blank
line, and there are three segments, so the while loop executes three
times. If you don't have Digest::MD5 installed, you get to see what
happens when "customer code" fails :)

However, as Uri said, using a hash is best. The difference between
${USER} and $h{USER} is nominal.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top