Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

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

by Laurent_R (Canon)
on Nov 22, 2017 at 19:04 UTC ( #1204059=note: print w/replies, xml ) Need Help??

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


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.

Replies are listed 'Best First'.
Re^4: Creating dispatch table with variable in function name
by dsheroh (Monsignor) on Nov 23, 2017 at 09:48 UTC
    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.)


      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.


      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.

Log In?

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

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2022-08-07 19:10 GMT
Find Nodes?
    Voting Booth?

    No recent polls found