Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Re^2: Creating dispatch table with variable in function name

by dsheroh (Monsignor)
on Nov 22, 2017 at 08:18 UTC ( #1203994=note: print w/replies, xml ) Need Help??


in reply to Re: Creating dispatch table with variable in function name
in thread Creating dispatch table with variable in function name

Dispatch tables (hashes with coderefs as values) are a relatively well-known "advanced" technique in Perl and pretty widely used and respected. Personally, I'm using it a lot these days to create programs with a "plugin"-type structure, but it's also used heavily in web frameworks or other contexts where you normally receive a string from an outside source (the UI, a config file, the other end of a network connection, etc.) telling you what to do, then look that string up to find out how to do it. It's simpler, more extensible, and more efficient to match the command to the implementation with a hash lookup than a long if-elsif chain.

Regarding your concern about it seeming "sort of similar to symbolic references", the most common response I see to "use a variable as a variable name" questions is that you should instead put your data in a hash and look it up by name that way. This is the exact same thing. The hash just contains coderefs instead of static data values.

Replies are listed 'Best First'.
Re^3: Creating dispatch table with variable in function name
by Laurent_R (Canon) on Nov 22, 2017 at 19:04 UTC
    Hi,

    I did not have time before to answer in a detailed fashion.

    This is an example (under the debugger) of the use of symbolic references:

    DB<1> ($blue, $red) = qw / b r/; DB<2> say ${$_} for qw /blue red/; b r
    What is happening here is that the second line constructs the variable name $blue from the string "blue" (and the same for red). It works and prints the values of the variables initialized in the first line. It works, yes, but we all know that this technique is very much frown upon, to the point that it is forbidden under the strict pragma, as shown with this one-liner:
    $ perl -Mstrict -E 'our ($blue, $red) = qw / b r/; say ${$_} for qw / +blue red/;' Can't use string ("blue") as a SCALAR ref while "strict refs" in use a +t -e line 1.

    Why does it work? Because it is constructing an entry (variable name) that can be found in the symbol table.

    You can even construct the name with concatenation, doing it this way:

    DB<3> say ${ "bl" . "ue"}; b

    Now the reason I was worried about the OP's code is that the way it constructs the subroutine name is quite similar, and with essentially the same intent: to create a name and look up the symbol table for the subroutine name.

    Again, I'm not saying this is bad, I'm just asking other monks what they think about it.

      I see your point (and obviously misunderstood what you were questioning when I posted my earlier reply), but I'm not convinced that the equivalent
      my %dispatch = ( first => \&_create_first, last => \&_create_last, user => \&_create_user, id => \&_create_id, email => \&_create_email, create_password => \&_create_create_password, );
      is any more virtuous than the map in the OP. Both have the same potential for run-time errors if one or more of the referenced subs doesn't actually exist - simply naming them explicitly does not turn it into a compile-time error:
      $ perl -E 'use strict; use warnings; my %d = ( foo => \&foo ); say "OK +"' OK
      Retyping _create_ every time doesn't actually buy you anything aside from maybe an eyeball error check. (_create_create_password does look a little suspicious to me, but it might still be correct in the OP's code.)

        dsheroh:

        I'll call it slightly more virtuous than the map version, and my reasoning is this: I can use grep and find all references to the function(s). On the other hand, if your lookup keys were the same as the function names, it would be a wash IMHO.

        Having said that, in my personal code, I generally use magical code in lots of places, including using map to build dispatch tables as you're doing. But for production code, a little less magic is appreciated when trying to locate things like function references.

        Experimenting with techniques to reduce typing and improve clarity is one of the areas in programming that I find very fun. When we come up with interesting new ways to simplify/clarify some of the menial tasks, we can build new idioms and improve the overall programming experience.

        ...roboticus

        When your only tool is a hammer, all problems look like your thumb.

        Yes, dsheroh++, I agree: quite possibly it isn't much more virtuous, and yes, you might still very well refer to a sub that does not exist and the compiler won't tell you about it.

        On the other hand, it is much easier to check that all your subrefs correspond to actual subs when they are explicitly listed in the dispatch table as in your example than when their names are dynamically created, and it is also probably easier to make sure that your tests cover all of them.

        Also, if we insist that symbolic references for package variables should be avoided (at least for "usual" programs, I am not talking here of pieces of white magics introduced for extending the language), then we should presumably also try to avoid similar constructs for subroutines.

        As I said already, I am not saying that this is bad, I am just asking monks for their opinions on this subject. I have actually done similar things a couple of times, and even explicitly written things to the symbol table in order to extend the language (by replacing a subroutine by another one). I don't think I would do that for regular plain-vanilla coding.

Re^3: Creating dispatch table with variable in function name
by Laurent_R (Canon) on Nov 22, 2017 at 08:50 UTC
    Thanks for your answer. I am not objecting at all to using dispatch tables, I'm using dispatch tables quite regularly. What I am possibly worrying about is that the subroutine ref names are dynamically created by concatenating various peaces of the name and enclosing them in in &{...} construct.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1203994]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (3)
As of 2022-08-15 04:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?