Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW

using eval or equivalent to automatically generate my statements

by ISAI student (Scribe)
on Dec 13, 2012 at 13:44 UTC ( #1008660=perlquestion: print w/replies, xml ) Need Help??
ISAI student has asked for the wisdom of the Perl Monks concerning the following question:

Hello all. I am doing reshape of an old, 16,000 lines of 1 file PERL script, riddled with global variables, to a more modular, use strict compatible packages files. It even less fun than it sounds. I have come to the situation where I try to auto-convert the global variables to local ones. As some of these variables are hash of hash of hashes, this thing can be very long, and hard to automate.

What that I have come to think that instead of rewriting it from scratch, is to have all of the previous global variables defined as "my" in the main loop, have a hash that would collect them, then pass a reference to the hash to thes subroutines in the file. I.E. In the main loop, there is a global variable, $foo. Now I can converting it to a hash passable, my variable

my $foo; my %hash; $hash{'$foo'}=\$foo; sub1 (\%hash);
Then, in the sub sub1:
sub sub1{ my $ref_from_global = shift; my $foo=${$$ref_from_gloabl{'$$foo'}}; }
What that I would like to have is a subroutine that can generate all these my variables (there are well over 50), w/o resorting to copy paste it all the time. I.E.
sub sub1 { my $dat_ref = shift; my $code_ref = shift; eval {&\code_ref); }

When the line eval {&\code_ref);, is eqauivalent to the line above, and getting variables generated within the same scope. Is that possible?

Replies are listed 'Best First'.
Re: using eval or equivalent to automatically generate my statements
by Athanasius (Chancellor) on Dec 13, 2012 at 14:17 UTC

    Hello ISAI student,

    To expand a little on tobyink’s answer:

    My reaction to your question is: Take a step back, and look at the wider picture. Modularity is an excellent goal, but why? Because a script composed of self-contained modules, loosely coupled together, is easier to debug/maintain/enhance than a monolithic script “riddled with global variables” (to use your words). But conversion of global variables into lexical variables is a means to this end, not a goal in itself. The aim is to produce modularity and loose coupling: global variables shared across a script entail tight coupling.

    Now, I could be wrong, but it seems to me that your proposed solution is to retain global variables in all but name — to have the same variables shared across the whole script, but to make these variables lexical via a monster hash which is passed by reference to all the subroutines. The likely result is that you will end up with the same tight coupling as before, but implemented in a more complicated syntax which is even harder to debug/maintain/enhance than the original.

    The bottom line is this: If you want the benefits of modularity, you must have loose coupling. And to achieve loose coupling, you must identify which variables need to be shared between modules, and refrain from sharing any which do not. For this, there is, alas!, no easy fix — you have to re-design and re-factor before you can re-code. And the measure of success is this: the end result must be simpler and clearer than the original.

    I know this advice is not what you were hoping to hear, but I think it’s good advice all the same. I hope it helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      The answer is yes and no. refractoring the entire script is not an option for me right now. The script is with some bugs and not all that stable. The coupling may not be made looser for the global variables, but still, fixing bugs and adding functionality is easier on the human mind when the code is broken into many files, and use strict is applied. Although actually two monster hashes are used, the rest is very loosely coupled, instead of having almost everything coupled. The added complexity due to a few hash deferencing is linear. The added complexity of a multitude of lines of code and not using use strict is exponential (at least to me, I am an EE, not CS person). Even in Ruby, the self proclaimed OO queen, one can still globally loop on all instances of a certain class. This is a lexical analog , in a sense, that I have just 1 instance of this 1 "class". Some things, in any script that I know, are global (environment variables, for instance), this is, in a way for the usage fo these two hashes, similar. I need the management buy in to do what you suggest, and not w/o reason, I do not have it. I do understand where you come from, and would very much like to accept your offer, but it's not just up to me. Thank you for time.

        Yes, I can see that re-design is not an option for you at the moment, and I agree that modularising and adding use strict will aid both debugging and enhancement.

        But why make it all so complicated? You can have lexical variables which are strict-compliant and still package-global — just use our instead of my:

        # File: #! perl use Modern::Perl; use Foo; our $Id = 42; Foo::foo_id(); say 'In main, $Id is ', $Id; ... # File: package Foo; use Modern::Perl; sub foo_id { say 'In Foo, $main::Id is ', $main::Id; $main::Id = 17; } 1;


        In Foo, $main::Id is 42 In main, $Id is 17

        You can of course get fancier with @EXPORT, @EXPORT_OK, etc., but using the fully-qualified package names is probably a better strategy as it makes the trail of the global variables self-documenting.

        Hope that helps,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: using eval or equivalent to automatically generate my statements
by tobyink (Abbot) on Dec 13, 2012 at 14:14 UTC

    You appear to be trying to emulate the concept of global variables with local variables. Why?

    Global state is (generally) bad. Achieving global state using only local variables might be an interesting experiment, but it doesn't make global state any less bad.

    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      Thank you for all yor help. Unofortunaely, I was granted very short time to do a rather thankless task. Theoretically, scratching it and start anew is the way to go. I have no management green light to do so, and the decision is not without reason. It took > 5 years of on-going user feedback to get where we wanted.

      I was hoping to see some really cool PERL magic, it may be for the best, but it would have been one cool metaprogramming feat...

        OK, so as not to disappoint...

        Introducing Acme::Asplode: it lets you drag your caller's lexical variables kicking and screaming into your sub. Ought to work in Perl 5.12+.

        Example usage...

        use 5.012; use strict; use warnings; use Acme::Asplode; sub bar { asplode($x, $y); # steal lexical variables from our caller! say $x; say $y; asplode @A, $z; # steal moar lexicals!!! say $z; $z++; # alter our caller's lexical variable!! say for @A; }; sub foo { my $x = 1; my $y = 2; my $z = 3; my @A = qw(a b c); bar(); say "Z is changed: $z"; } foo();

        Update: Acme::Asplode, renamed Acme::Lexical::Thief is now on CPAN.

        perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: using eval or equivalent to automatically generate my statements
by Anonymous Monk on Dec 14, 2012 at 09:29 UTC

    Does your script have tests?

    Before you make any changes you should
    1) know what the program is supposed to do
    2) know what it actually does currently
    3) know the difference between what it does and what its supposed to do (a buglist)
    4) have an automatic way (a programmatic way) of checking the program does what its supposed to do

    I would start by writing tests :) saving all changes in version control

    Then maybe use B::Xref to identify most isolated chunks to turn into good and proper subs that take arguments and return values, and take on increasingly messier stuff

    Now, PadWalker can help you get at the stuff, and you an use local or even Data::Alias

    get lexical via PadWalker/Lexical::Util, and globals(locals) via %package::

    You might try Binding

    This may be of interest Splitting a project into smaller files, general advice finding duplicate code

    Good luck

      Yes, it has tests, I've refused to touch it w/o them. I'll have a look at the references. The company I work for use svn, and I already use it. I've learned to use VC, the hard way. Thanks!

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1008660]
Approved by Athanasius
[Corion]: WTF? There is a dev release of Danga::Socket?! I thought that module died a long time ago?! According to its changes, the last release was in 2008 ;)
[Corion]: (and Brad Fitzpatrick long since moved on to Go ;) )
[Corion]: But who am I to worry about necrocpan - I'm revisiting my online banking module, and also I'll somewhat revive WWW::Mechanize:: Firefox, at least if the test suite passes under the Waterfox browser ...
[Corion]: Maybe I should look through my CPAN releases and revisit them in order of last release and think about what to do with the modules :)
[marto]: this cpan day there should be a call to remove old crud :P
[marto]: as well as encourage active development

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (8)
As of 2018-07-17 08:18 GMT
Find Nodes?
    Voting Booth?
    It has been suggested to rename Perl 6 in order to boost its marketing potential. Which name would you prefer?

    Results (359 votes). Check out past polls.