Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Usage of 'my' in 'eval'

by Anonymous Monk
on Jan 06, 2006 at 11:35 UTC ( #521448=perlquestion: print w/replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Is my understanding of eval correct as to after having eval'd some perl code introducing my variables, they go out of scope directly afterwards?

What I mean written in code:

{ my $code = "my $a = 1;"; # current scope doesn't have a clue what $a is "yet" eval $code; # current scope doesn't have a clue what $a is "any more" (?) }

I'm trying to export lexically scoped (i.e. my variables) via eval which haven't been and are not to be defined in the code scope before.

Global scoped variables are not of any help to me, nor local variables to be defined in forehand; what I want are lexically scoped variables in the lexical scope I execute eval in.

Is there any way to do this (WITHOUT having to define them before executing eval)?

Thank very much for any help!

Replies are listed 'Best First'.
Re: Usage of 'my' in 'eval'
by Perl Mouse (Chaplain) on Jan 06, 2006 at 11:43 UTC
    An eval provides its own 'scope', so anything lexical you do is limited to that scope.

    But even if it weren't, it wouldn't do you much good. The biggest effect 'my' has is at compile time - it tells the compiler you have a lexical variable. However, the eval happens at run time - if you'd use '$a' after the eval, '$a' is unknown at compile time (so, if 'use strict' is in effect, it will complain).

    Perl --((8:>*

      I was afraid it'd be so!

      Thank you very much for the quick help!

Re: Usage of 'my' in 'eval'
by ysth (Canon) on Jan 06, 2006 at 12:11 UTC
    Update: following solution assumes the poster wants the lexicals to apply to some code being eval'd ("WITHOUT having to define them before executing eval" implying that there is such an eval); a different reading of the question is that the lexicals should apply to the code following the eval, which isn't directly possible.

    Just append them to the code to eval?

    my @lexicals = map "my \$$_", qw/foo bar baz/; my $code = '$foo = 42; $bar = 13; sub { $baz += ++$bar + ++$foo };'; my $result = eval join(";", @lexicals, $code);
    But I think you are telling us how you think your actual problem should be solved; what is your actual problem? There's likely to be a better way.

      Thanks for your interest!

      What am I trying to do?

      I'm hesitating a bit telling about it, since I don't think "perl folks" will like what I try to implement. This is by no means meant as an offence, rather shall indicate me being one of those "refugees from another language" (mainly Java, in my case) who's trying to make perl work in a non-perlish way!

      Nevertheless, I'll try to explain:

      Implementing a little "bigger" piece of software in perl (Yes, I'm also using modules, objects, and inheritance! Who guessed? Some of the my Java leftovers... (-; ), I have had repetitive problems with my parameter passing to my functions, some of it caused by perl's autovivificating nature when passing misspelled hashes, some by me getting confused with all your perl's $, @, %, references and the like. Besides that, some of the function lists with multiple parameters started getting obfuscated, thus I decided to use "named parameters", i.e. passed my arguments aligned with their name and "processed" them in a local parameter hash.

      I hope you won't fall off your seats when reading the following code (being completely Java style, I know, probably being a sin using perl builtins with paranthesis also), but in case you feel any masochistic drive inside your personality...:

      sub new { # parameters: # [$this], # @attributes: tree, messageNum, messageText my ($this, @attributes) = @_; # "process" optional attributes my %attributes = ( tree => undef, messageNum => undef, messageText => undef, @attributes, ); # assert for valid argument list if ((keys %attributes) > 3) { die "invalid argument list during function call!\n"; } # assert mandatory parameters my $tree = $attributes{tree}; my $messageNum = $attributes{messageNum}; my $messageText = $attributes{messageText}; if ((!defined($this)) || (!defined($tree)) || (!defined($messageNu +m))) { die "invalid argument list during function call!\n"; }

      The first assertion is supposed to tell me whether I misspelled one of the parameter names (which simply would be "ignored" otherwise), the second is to check whether the mandatory parameters truly have been passed as arguments and correctly been set (okay, at this point you might want to ask why I don't rather try to treat my senility than trying to make perl a nurse. But I told you I didn't want to tell in the first place, didn't I?).

      Since this piece of code is quite repetitve, I tried to automate it some, i.e. especially trying to make the creation of my local arguments easier, and came up with the follwing:

      sub new { # parameters: # [$this], # @namedParameters: tree, messageNum, (messageText) ##### process parameters ##### my ($this, @namedParameters) = @_; # process named parameters (with default values) my ($tree, $messageNum, $messageText) = assertNamedParameters([ numNamedParameters => 3, tree => undef, messageNum => undef, messageText => undef, ], \@namedParameters ); # check mandatory paramters assertValueIsDefined($this, $tree, $messageNum);

      The assertNamedParameters may seem a little bit odd and misformated (okay, I'm not quite finished with my code cleanup). The intention is to define the valid parameter list and provide default values for each of it (so passing them CAN be optional).

      If of any interest, the function itself looks like this:

      sub assertNamedParameters { # parameters: # $parametersRef, $argumentsRef my ($parametersRef, $argumentsRef) = @_; # check passed arguments assertValueIsDefined($parametersRef, $argumentsRef); # create parameter hash my %parameters = ( @{$parametersRef}, @{$argumentsRef}, ); # get valid number of parameters if (!exists($parameters{numNamedParameters})) { assertThrowException( "parameter key ('numNamedParameters') has not been defined in passed parameter hash"); } # get number of parameters my $numNamedParameters = $parameters{numNamedParameters}; # assert for valid parameter count if ($numNamedParameters < 1) { assertThrowException( "invalid number of parameters ($numNamedParameters) specif +ied (minimum is one parameter)"); } # assert parameter list for valid parameter count # (account for the numParameters key itself (!)) if ((keys %parameters) != ($numNamedParameters + 1)) { assertThrowException( "invalid number of parameters ($numNamedParameters) passed +"); } # return parameter values my ($parameterName, $parameterValue, @parameterValues); # retrieve each given parameter # (skipping 'numParameters' key) for (my $i = 1; $i <= $numNamedParameters; $i++) { # retrieve parameter name from parameter list # IN ORDER concerning given parameter list! $parameterName = $parametersRef->[2*$i]; # get parameter value from parameter hash $parameterValue = $parameters{$parameterName}; push(@parameterValues, $parameterValue); } return(@parameterValues); }

      The aim is to return the arguments in the order specified by caller, thus being passed into his local variable correspondingly.

      (Here's where the question starts:)

      What I thought of now (and what originated my question) was to simply return an code string setting up the named parameter variables and it's according values, with the caller then simply eval's.

      I hope I could explain what I'm trying to do (please don't ask me WHY I would want to do that, probably my medication just has been wrong today, or I simply smoked something REAL good! (-; ), thus I wonder if it can be done in any handy way?

      A last comment to answer the surely upcoming question in advance: I HAVE TO use perl for programming this application, otherwise I surely wouldn't try to abuse it, but programming in some language I'm more used to.

      And I'm concerned with some rather complex data structures including trees with doubly linked (and cached) leafs, this subtle errors in the parameter passing are hard for me to loacte and debug.

      So, if anybody had the patience and mercifulness to read ALL THIS, be assured my heart felt thanks! I'd be glad to hear anything!

      I hope my english is understandable. Greeting from Germany!

        The short answer is that you can't do this using eval the way you want to because Perl needs to know at compile time whether a variable you're using is a global or a lexical, so it doesn't make sense to declare lexical variables in eval'd code and then use them in non-eval'd code in the same block. But I don't think you need anything so drastic.

        I would start at the most basic level. It sounds like what you're trying to do can be described simply as "assign some lexical variables to values passed in through a hash, and make sure they're defined." The simplest way to do the assignment part is

        my ($self, %values) = @_; # we're being passed a hash so let's treat +it like a hash my ($tree, $messageNum, $messageText) = @values{qw/tree messageNum mes +sageText/};
        So far so good. But we also need to insure that a) the parameters are all there, b) there are no extraneous parameters, and c) they all have defined values. This we can abstract into a subroutine like this:
        sub validateParams { my ($data, @paramNames) = @_; die "first argument to validateParams must be a hash" unless ref +$data eq 'HASH'; my $expect_count = @paramNames; my $actual_count = keys %$data; die "incorrect number of parameters - expected $expect_count, got + $actual_count" unless $expect_count == $actual_count; for my $name (@paramNames) { die "parameter $name missing or undefined" unless defined $da +ta->{$name}; } }
        and we can call it like this:
        { my @paramNames = qw/tree messageNum messageText/; sub new { my ($self, %values) = @_; validateParams(\%values, @paramNames); my ($tree, $messageNum, $messageText) = @values{@paramNames}; } }
        I put it in an enclosing block like that so that each subroutine can have its private list of required parameter names set up in advance.

        There are also some modules on CPAN for this such as Params::Validate.

        I don't think that qualifies as "abuse" of perl. A lot of perl code uses named parameters but doesn't validate what is passed very well. So the idea of eval "" was to avoid mentioning the names more than once, and you've settled for mentioning them twice now?

        You may want to look at Params::Validate.

Re: Usage of 'my' in 'eval'
by mce (Curate) on Jan 06, 2006 at 19:54 UTC
    Be careful when testing with $a and $b. This might be confusing. See node 97651.

    Dr. Mark Ceulemans
    Senior Consultant
    BMC, Belgium

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://521448]
Approved by Corion
Front-paged by Roy Johnson
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2018-11-17 06:20 GMT
Find Nodes?
    Voting Booth?
    My code is most likely broken because:

    Results (202 votes). Check out past polls.