Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Imagination greater than reality?

by writch (Sexton)
on Jul 10, 2017 at 21:54 UTC ( [id://1194749]=perlquestion: print w/replies, xml ) Need Help??

writch has asked for the wisdom of the Perl Monks concerning the following question:

I thought that there's probably some kind of dynamic way to use a library, but I haven't found it yet. Most of my attempts end up being a syntax error, or don't error but don't work.

My idea is that I can have libraries for different states so that I can calculate a value differently in Texas than in Georgia.

Obviously, this doesn't work, but it's what I've got in my head:

my $state = 'TX';
use State::$state::formulas;
Is what I'm thinking of outside of the current box, or am I just doing the right thing the wrong way?

Replies are listed 'Best First'.
Re: Imagination greater than reality?
by 1nickt (Canon) on Jul 10, 2017 at 22:10 UTC

    Using the core module Module::Load:

    use Module::Load; my $state = 'TX'; my $module = 'State::' . $state . '::formulas'; eval { load $module; } or do { my $err = $@; ... };
    There are more ways to do it, this is one :-) (update: huck showed an even lower-level one while I typed, ++)


    The way forward always starts with a minimal test.
Re: Imagination greater than reality?
by huck (Prior) on Jul 10, 2017 at 22:05 UTC
      I want to thank everyone for the thoughtful and creative input. I ended up using this method. It's funny, but as I was typing in the original post last night I was wondering if joining the string in that fashion would work.
Re: Imagination greater than reality?
by Marshall (Canon) on Jul 10, 2017 at 22:39 UTC
    The solutions from huck and 1nickt look fine to me. Note: BrowserUk's idea about require is also applicable to this problem.

    I don't know whether you need the flexibility of different algorithms between states or just different parameter values, like "state sales tax" or whatever.

    A different approach to consider might be the SQLite DB. This DB uses a simple file instead of a separate SQL server process. It works very well (I think every smartphone has a SQlite DB). The Perl DBI for SQLite is excellent and I've used it on a number of projects; some small and some with millions of records. Your module could access the DB to get values for whatever state you tell it you are in.

    Also one point to consider is maintenance. In general maintaining a single US DB is probably easier than 50 different state specific modules expressed as Perl code. I know very little of your actual application. If this sounds like it is worth pursuing for you, happy to provide more information and suggestions.

    Update:
    Another try at an explanation: If you can express the functions for each state as a single set of "code" with 50 different sets of "data", I would recommend the DB approach even if this complicates the code logic in this "single set" of code. If you have 50 different sets of code, that can vastly complicate the maintenance.

    What you apparently want to do may adopt itself well to an OO paradigm. The user program says: "use StateFormulas;". When creating a new StateFormula object, specify the state. When the object is created, data is read from the DB. This object for say TX (Texas) now works differently than one for say IA (Iowa) because of differing parameters. If say the Texas calculations are somehow different because it has a coastline, then I would put a field in the DB like "has_coastline". The code will perhaps wind up some "if" statement, but that if statement will apply to all states with a coastline. If you go this way then code could wind up with easy comparisons between different states because each object in the program would be specific to a state rather than saying "hey now this program works specifically for TX". Hope this helps.

      I don't know whether you need the flexibility of different algorithms between states or just different parameter values, like "state sales tax" or whatever.

      FYI, some states have local as well as state-wide taxes. Also, different categories are taxed differently in different states, sometimes even different local taxes. I would not be surprised if it were significantly easier to write code for each state than to try to devise a set of tax tables.

        I would not be surprised if it were significantly easier to write code for each state than to try to devise a set of tax tables.
        Actually I would expect that maintaining 50 different sets of code would be significantly more effort than a single set of code with a DB, even if the code is significantly more complex to write in the first place. 50 different algorithms is a difficult thing to get one's brain around. If say some flaw is found in the Texas algorithm, then it could be a big problem figuring out which of the other 49 sets of code are affected.

        Another factor can be just the on-going updates of the per state information. I doubt the OP is working with "sales tax" - that was just the first obvious "per state" idea that popped into my brain. There are companies who specialize in DB's and software to deal with the complex plethora of US tax laws. A long time ago I had a friend who was a salesperson for a company like that. What multi-state and multi-national companies do with this tax stuff is super complicated and worth "big bucks". For a number of reasons, "roll your own tax code" is not a good idea. My mention of this was just a "for instance, example", not anything deeper than that.

        Back to the generic programming issues... Whatever it is that changes between the states, a solid program "product" will have a way to keep things "right" and "updated" past the first program release. As a dev engineer, I want to write new code that solves new problems. I can't do that if "my released product code" is not maintainable or extensible by somebody else.

        I have no problem with the solutions to the OP's problem which address it directly - they look fine to me. My goal was to present another possible approach to the OP's problem.

        One issue is "hey, its been 2 years, how do I know that this per state stuff is "up to date"? How does somebody get "notified" that something needs to change for Texas? As another issue, an OO paradigm seems like a good idea here. I would not put knowledge of the states into the stateObject. I would use some parameter like 'TX','AX','NTX'(North Texas) when creating the "StateObject" and give a clear error message comprehensible to the expected "user". The new StateObject looks into the DB and initializes its behavior based upon that. If Puerto Rico, PR ever becomes a state (which it probably won't), add another line to the DB. The testing code can make 50 different state objects and run the same data against those objects and compare results. A situation where the program starts and then is "customized" to a specific state is not as flexible of an approach.

        From what I have read, the OP is proceeding with a solution that works for him. Fine. There is often "more than one right answer" here. My comment are for others who may follow with similar issues.

Re: Imagination greater than reality?
by BrowserUk (Patriarch) on Jul 10, 2017 at 23:14 UTC

    Try require.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Imagination greater than reality?
by kcott (Archbishop) on Jul 11, 2017 at 06:42 UTC

    G'day writch,

    "Is what I'm thinking of outside of the current box, ...?"

    Not outside the current box, or even outside an old box: the builtin module File::Spec, for instance, has been doing this sort of thing for years. [I'm fairly sure it's more than a decade, but I don't have specific information to hand.]

    "... or am I just doing the right thing the wrong way?"

    If you're getting errors, or it's not working as expected, then probably "the wrong way". :-)

    I would consider reordering the elements of your namespace such that the least specific element is first and the most specific last. It's your module, you can call it whatever you like, and there may be aspects of which I'm not aware; however, I probably would have chosen:

    Formula::State::$state

    You can keep generic code in Formula::State, perhaps something like:

    sub calculate_xyz { my ($x, $y, $x, $state_variance) = @_; $state_variance //= 1; return (($x + $y) / $z) * $state_variance; }

    Then in Formula::State::$state:

    ... calculate_xyz($x, $y, $x) ... # Use standard value ... calculate_xyz($x, $y, $x, 1.5) ... # Add 50% to standard value ... calculate_xyz($x, $y, $x, 2) ... # Double standard value

    That also keeps your state-related formulae separate from Formula::Molecular, Formula::Secret, and so on.

    You're showing use, which suggests that you want to load your module at compile time. You can do that with something like this:

    BEGIN { my $state = ... get state from somewhere (e.g. $ARGV[0]) ... require "Formula/State/$state.pm"; "Formula::State::$state"->import(@optional_arguments); }

    I ran a quick command line test (mainly to check the syntax I'd given you):

    $ alias perle alias perle='perl -Mstrict -Mwarnings -Mautodie=:all -E' $ perle 'BEGIN { my $x = $ARGV[0]; require "List/$x.pm"; "List::$x"->i +mport("uniq") } my @u = uniq (1,1,2,3,3,3); say "@u"' Util 1 2 3 $ perle 'BEGIN { my $x = $ARGV[0]; require "List/$x.pm"; "List::$x"->i +mport("uniq") } my @u = uniq (1,1,2,3,3,3); say "@u"' MoreUtils 1 2 3

    I initially used the first() function, which I thought was in both of those modules. It's not, so I got an error; however, that's also useful feedback.

    $ perle 'BEGIN { my $x = $ARGV[0]; require "List/$x.pm"; "List::$x"->i +mport("first") } say first { not $_ % 2 } 1, 2, 3' Util 2 $ perle 'BEGIN { my $x = $ARGV[0]; require "List/$x.pm"; "List::$x"->i +mport("first") } say first { not $_ % 2 } 1, 2, 3' MoreUtils Could not find sub 'first' exported by List::MoreUtils at -e line 1. BEGIN failed--compilation aborted at -e line 1.

    — Ken

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1194749]
Approved by hippo
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-04-24 21:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found