in reply to Ordering of parameters - Re^9: Converting Unicode
in thread Converting Unicode

This node falls below the community's threshold of quality. You may see it by logging in.
  • Comment on Re: Ordering of parameters - Re^9: Converting Unicode

Replies are listed 'Best First'.
Re^2: Ordering of parameters - Re^9: Converting Unicode
by choroba (Cardinal) on Dec 21, 2023 at 20:06 UTC
    > is this what programmers usually do? . . . put all variables in a hash and bounce them around to the various subroutines, even if a given subroutine might use but a few of those variables?

    No. That's not what we do. We use so called "named parameters". See for example the section of the same name in the Modern Perl book.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re^2: Ordering of parameters - Re^9: Converting Unicode
by SankoR (Prior) on Dec 21, 2023 at 22:18 UTC
    Regarding the hash suggestions, is this what programmers usually do? . . . put all variables in a hash and bounce them around to the various subroutines, even if a given subroutine might use but a few of those variables?
    That was my suggestion and, no, only pass arguments that the specific sub requires and nothing more. This is what you should be doing regardless of how the data is being passed. If you've been passing all your data around even if a given subroutine might not use it, I gotta agree with Jenda, this sounds poorly designed.

    Apparently, I'm the only one who ever needs scores of variables passed into a single subroutine...
    But do you really need scores of variables passed into a single subroutine? You might not be the first to try but you're the only person I've seen determined to continue doing that while blaming the language and looking at time tested solutions with 'disgust'. I seriously thought you meant you had 5-10 arguments which would be hard to keep track of but fifty?!? I can't imagine a well designed sub that changes or even accesses the value of fifty different variables every time it's run. At the risk of making another 'disgusting' suggestion, if most of those 50 arguments is just state data being read or passed along to other subroutines, objects might be what you're after.

    Regardless, 30,000 lines of code with dozens of globals and subs that accept 50 arguments is madness. Go ahead with the rewrite but an inexperienced developer who has generated a codebase larger than both Mojolicious and Moose while scoffing at established best practices and decades old solutions even when they're explained will not suddenly write better or smarter code by switching to Raku (yet another language you don't really know). We've been trying to help and I'm not going to bully someone for being inexperienced but every complaint you have sounds like a symptom of poorly designed code [edit: and unjustified stubbornness] and I'm beginning to think we're just feeding a troll.

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re^2: Ordering of parameters - Re^9: Converting Unicode
by Bod (Parson) on Dec 22, 2023 at 00:43 UTC
    Apparently, I'm the only one who ever needs scores of variables passed into a single subroutine

    No, you are certainly not the only one...
    I recall writing a sub that took 20-odd parameters. It quickly got out of hand. At that time, I didn't know how to do it any other way.

    If I have a sub that takes more than two required parameters and one optional parameter, I would use a hashref instead so I could pass a series of unordered, named parameters. However, if I got to that point, I would seriously question whether I had created a suboptimal fudge instead of a good design.

    The one time I have recently wanted to pass many parameters to a sub was the components of an email (from name, email, recipient name, etc). I created a sub that accepted a hashref and then encapsulated the sub in a module so the functionality was clearly separate from the main code.

    If you are writing subs that take lots of parameters, I suggest you ask yourself if there is a better way to do it. The answer, in any language, is probably yes.

Re^2: Ordering of parameters - Re^9: Converting Unicode
by bliako (Abbot) on Dec 22, 2023 at 08:18 UTC
    Apparently, I'm the only one who ever needs scores of variables passed into a single subroutine

    No you are not. BUT pause a moment and think if those variables are just general configuration, settings which can span hundreds of variables. For example, often my subs require a verbosity value and a logger object (or filehandle) to dump debugging information. I can code these two variables into the signature of any sub. And call the sub like: distance($from, $to, $verbosity, $logger). The last two are optional though, if they are undef then it just logs nothing.

    Inevitably, at some future time I need to refactor and add another parameter, let's say $timeout, and converting the sub to distance($from, $to, $verbosity, $logger, $timeout). It is a headache to refactor all my code to accommodate this new variable.

    In the above, verbosity, logger are optional. The way I handle the complexity is to call them system-wide configuration. And put them into a hash. This hash is created at runtime by reading a configuration file:

    $config = { verbosity => 3, logger => new Logger(), ... # hundreds of configuration settings };

    And it is now passed around the subs like distance($from, $to, $config). If I need to add a new config variable like timeout, there is no big refactoring. Or, the refactoring is the same but now it is optional and can be done just for those subs that need the new setting, as the needs change.

    But then I need to sometimes override a config setting for just one sub, e.g. the timeout. So, why not pass a hash(ref)? Something like this:

    $params = { from => ..., to => ..., # override the timeout of the system-wide config timeout => ..., # this is the config read from file config => { verbosity => 3, logger => ..., timeout => 5, ... } }; sub distance { my $params = shift; # the timeout value will be the config's unless # caller passed their own value to temporarily override it my $timeout = exists($params->{timeout}) ? $params->{timeout} : $params->{config}->{timeout} ; ... # similarly, I return back multiple values in a hash(ref) return { errormsg => 'nothing', status => 1, # all OK! distance => 42 }; } # call it my $ret = distance($params); die $ret->{errormsg} unless $ret->{status} == 1;

    Now, suppose that a sub needs to save some results for access by other subs to be run later. Well, why not save that into the config under $config->{stash}->{results-of-distance} = { ... }

    But what I am showing here is just reinventing the OOP wheel:

    # I am skipping some boilerplate package Calc; sub new { my ($configfile, ...) = @_; my $config = read_configfile($configfile); my $internal_data = { config => $config, stash => {} }; # bless etc. } sub distance { my ($internal_data, $params) = @_; # internal_data holds config, # $params holds from, to and anything to override # config just for the life of this call ... # optionally save results to the stash $internal_data->{stash}->{distance} = 42; # return something back return { errormsg => 'nothing', status => 1, # correct distance => 42 }; }

    And you use this as:

    my $calcobj = Calc->new('settings.config'); my $res = $calcobj->distance({ from => ..., to => ..., # optionally temporarily # override system-wide config setting timeout => ..., });

    In the above example you do not have anything global which should be anathema to any programmer.

    All data is inside the object, encapsulated so-to-speak. You can have two of those objects running alongside each with a different configuration file. Pass these objects around and you pass: 1) their config data, 2) their state (stash), 3) their logic (subs, algorithms).

    I hope that was gentle. Note that I used hashrefs.

    bw, bliako

      +1 from me for that, bliako. Thank you. I'm sure others reading here will be helped by it. As for me, I'm lost at the very first line of your example code where it appears you are assigning a hash to a scalar. If I ever tried coding something like that, the program would likely put up a fatal error, and I would have no clue what I'd done wrong so as to fix it. I see code like yours out there regularly. It's just too much over my head, so I bring my head back under those clouds and do the best I can with what I can understand. I've read the OOP sections of "Programming Perl" multiple times, I've poured over others' code aplenty, yet my mind has never grasped it. My usage of OOP is limited to copy/pasting code that is sufficiently self-contained as to need little adaptation. It's a bit like copying the answers on a math test--if the teacher suspected me of cheating and asked me to do some novel problem, showing my work, I'd fail for sure.

      Essentially, I've got to know the ways to program without OOP due to my limitations--which means no use of classes, methods, blessings, arrayrefs, hashrefs, etc. unless I can just copy/paste a working solution from somewhere. I don't even understand the why of abstractions--like why in the world is it supposed to be better? Much less so can I grasp the how or the syntax of it.

      From what you're saying about using hashrefs, honestly, it sounds almost like a substitute for globals--just in a more "politically correct" form. But that probably shows I understand nothing about hashrefs.

      Blessings,

      ~Polyglot~

        I'm lost at the very first line of your example code where it appears you are assigning a hash to a scalar.

        I feel your pain!
        For a long time, this sort of syntax was alien to me. But it is worth taking time to understand.

        $config = { verbosity => 3, logger => 12, };

        This is assigning an anonymous hashref.
        Consider this instead:

        # Create a hash my %myhash = ( 'verbosity', 3, 'logger', 12 ); # Assign it to a hashref my $myhashref = \%myhash; # To get the same result # without the hash variable my $myhashref = { verbosity => 3, logger => 12, };

        For many years, references were beyond me.

        It is easy to just say that we don't understand them and dismiss that as the way that it will always be. This is what Carol Dweck describes as a "Closed Mindset". The alternative is to reframe the problem. Instead, say that we don't know how to do it yet but we can learn - it might be difficult, but it is possible. Then the only question is whether one is prepared to put in the effort and be humble enough to ask for help and receive wise counsel from those who provide it without getting defensive when information and methods that don't make sense are presented. Nothing makes sense until it has been learnt...that's why people used to think the Earth was flat...

        I asked here in the Monastery about references. Several Monks gave conceptual models that didn't make sense and examples that I didn't understand. I tried to use references in test code and failed. I tried changes, sometimes at random, until I got something working or, at least, giving a different error message. I asked if my understanding of what was happening was right. Slowly I grew to understand...

        You too can understand references, OOP and trigonometry...but it requires an open mindset...

        I'm lost at the very first line of your example code where it appears you are assigning a hash to a scalar.

        Please read perlreftut.


        🦛

        All is global in the computer's RAM!!!!! ;)

        A global variable is bad style not because it is just there but because you don't know who uses it. So, trivial example, if you change its name you rely on Perl's strictness to tell you who from those using it still use the old name which is now undefined and you need to fix it.

        $config in the line that confused you is only global in the computer RAM and in its limited scope when it was created from a file. ***I CHOOSE WHO SEES/ACCESSES THAT $config*** by passing it's reference (which is an address to the computer RAM which does not need to concern you at all) explicitly to them (subs). That's hugely different than declaring it global and any sub deciding to access it. Many times I revisit my code and can't remember a thing. Worst things happen when others work on your code. So, difficult to keep track where all the globals are used across my code. So, it is not political correctness. @^$^@* political correctness AFAIC. It is a way to deal with complexity. And a good one too.

        Anyway, I feel that I may confuse you, so I will not insist on this subject unless you ask.

Re^2: Ordering of parameters - Re^9: Converting Unicode
by Jenda (Abbot) on Dec 24, 2023 at 21:47 UTC

    I wonder what your background is. 30,000+ lines of code all in main package, and all able to access the same group of globals, all pointlessly declared with my? You do not need Raku, you need a beginner's programming book or a decent beginner's course. Otherwise you'll waste time rewriting the syntax details of your code while still programming in a subset of whatever your original language was. And frankly I wonder whether it was Fortran77 or Cobol.

    If there is a way to organize your code into files, then there is a way to organize the code into packages ... likely the same!

    Also it's not strict not working correctly, it's you not using it correctly.

    Jenda
    1984 was supposed to be a warning,
    not a manual!

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re^2: Ordering of parameters - Re^9: Converting Unicode
by NERDVANA (Priest) on Dec 26, 2023 at 19:17 UTC

    Any time I go beyond 5 parameters I make it into an object. That's all objects are really: a collection of named pieces of data, along with the functions that operate on them. Then, the attribute accessors help you avoid typos. If you know the names of all the variables upfront, then an object (or collection of objects) are the way to go. If the variables are dynamic, then yes just pass them around as a hashref. Or, do some clever hybrid of the two ideas if you have a special use case.

    Raku does in fact solve many of the pain points of Perl 5. That's its reason for existing. The reason many Perl 5 people did not jump on board was simply because of performance, and that it doesn't really carry forward the ecosystem of existing perl 5 modules. (Raku can use Perl 5 modules, but they are slower than native raku code, so that kind of implies that every existing module ecosystem ought to get rewritten into Raku) Also, many of us are perfectly satisfied with the tools we have available, and the minor feature gains don't justify the migration cost.

    If you want to solve your problems with Raku, then more power to you! If you are genuinely wondering why this crowd is satisfied with the features and warts of Perl 5 when it comes to problems like yours, then maybe you should make a large semi-anonymized example as a new post and ask "how would you solve this"? ...and then take the answers as a thing to study, instead of attack. I, for one, am using Perl 5 because I have not yet found a problem that it couldn't handle well, and it gives me the choice to solve the problem extremely quickly (toss hashrefs around) or very properly (object hierarchy and type libraries) or very performantly (using XS or Inline::CPP) or any combination I want within that triangle. I've tried many other languages and none have provided that versatility.

    (technically I'm cheating by using C, because that's a second language. but it integrates very nicely with Perl)

    Edit:

    Any time I go beyond 5 parameters...
    A lie. As I concluded, sometimes I play fast and loose, and other times I aim for more structure. It should read: "Any time I go beyond 5 parameters I think to myself that I should probably be using an object, and consider which of these parameters would make the most sense as an object, or whether they should get included as attributes in the current object, and whether I want to spend that much time refactoring".
    A reply falls below the community's threshold of quality. You may see it by logging in.