http://qs321.pair.com?node_id=869369

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

Dear Monks,

If $x="color", why isn't this line:

*{$x} = sub {#do something};

equivalent to:

&{$x} = sub {#do something};

Thanks

Replies are listed 'Best First'.
Re: typeglob/symbolic reference question
by BrowserUk (Patriarch) on Nov 04, 2010 at 00:43 UTC

    No. Your second example attempts to assign an anonymous subroutine to an lvalue sub called 'color'. If there is a sub called 'color' and it is not an lvalue sub you'll get:Can't modify non-lvalue subroutine call ... If it exists and is an lvalue sub, it would assign the address of the anonymous sub to its target lvalue.

    This $x = 'color'; *{$x} = sub {#do something}; is equivalent to sub color { #do something };


    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".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      I have a follow up question: how do you interpret the left side of the following line:

      *{"color"} = sub {#do something};

      "Go to the symbol table and look up the name color and grab the glob slot"?

      This is from Mastering Perl:

      Package    Identifier           Type    Variable
      
                             +------> SCALAR - $bar
                             |
                             +------> ARRAY  - @bar
                             |
                             +------> HASH   - %bar
                             |
      Foo:: -----> bar  -----+------> CODE   - &bar
                             |
                             +------> IO     - file and dir handle
                             |
                             +------> GLOB   - *bar
                             |
                             +------> FORMAT - format names
                             |
                             +------> NAME
                             |
                             +------> PACKAGE
      

        Maybe this clarifies things?

        sub bill { say 'hi' };; bill;; hi *bill = sub { say 'bye' };; Subroutine main::bill redefined at (eval 8) line 1, <STDIN> line 3. bill;; bye $x = 'bill';; *{$x} = sub { say 'hi again' };; Subroutine main::bill redefined at (eval 11) line 1, <STDIN> line 6. bill;; hi again

        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".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The symbol table entries are globs. They're associative arrays (like hashes) where the keys are data types. No, you're not grabbing what's in the glob slot of the glob, you're grabbing the glob itself.

        When you assign a reference to a glob, it gets assigned to the appropriate slot. *foo = []; assigns to the ARRAY slot; *foo = sub {}; assigns to the CODE slot.

        I guess, your confusion comes from the presence of GLOB slot in the symbol table entry described in the book. In reality, the syntax *{"color"} references the symbol table entry as a whole, not the GLOB slot of it. When you assign some reference to this entry, then perl copies the reference into appropriate slot of the symbol table (type of reference defines which slot is taken).

        The GLOB slot of symbol table entry mentioned in the perlref is something obscure. It is not reported by the Devel::Peek module. I guess, that this is just a syntax sugar. One can do either $a = \*other; or $a = *other{GLOB}; and both shall return the reference to the symbol entry as a whole. This reference can be used for reading files and doing something like *{$$a} = \$b; to alias $other with $b.

        Reference to the symbol entry is not the same as an "alias" for the symbol entry. You can do $a = *other; here $a shall become alias of the symbol entry and you can say ${$a}[0] = 2; or ${$a}{key} = 3; or <$a> to read the file. All of this will actually use @other or %other etc. Effectively, it is an alternative to using references.

Re: typeglob/symbolic reference question
by ikegami (Patriarch) on Nov 04, 2010 at 02:36 UTC

    What makes you think they might be equivalent?

    *{$x} = sub {};
    is no more equivalent to
    &{$x} = sub {};
    than
    *{$x} = [];
    is equivalent to
    @{$x} = [];

    In the first, you assign to a symbol table entry.
    In the second, you assign to a function call (or array).

Re: typeglob/symbolic reference question
by aquarium (Curate) on Nov 04, 2010 at 01:14 UTC
    unless absolutely necessary...mucking about with the symbolic table--just to implement a shortcut to a programming problem--is generally not a great thing. in this particular case i think a closure would be a cleaner solution. even if you're just learning about symbolic table by using some code..it's better to have example code that uses the symbolic table for good reason, or you could get into a habit of using symbolic table in a similar fashion to the artificial example provided. anyway, that's my rant about symbolic tables in perl.
    i like to read the ampersand in similar way to reading C, where it's literally "address of" operator. it's not exactly that in perl, but probably close enough for most purposes. so in that context the example line starting with ampersand would read: the address of an anonymous sub containing code/literal "color" is assigned the sub on the right hand side.
    but as per my rant...it would be less error prone and clearer to have a sub that takes the color as a parameter, and do something with that. with subs/functions things are easily extended..you get into a fight with symbolic table manipulations if you (for example) add similar code for handling (say) transparency and visibility etc other parameters. the main program starts looking like spaghetti code.
    the hardest line to type correctly is: stty erase ^H

      i like to read the ampersand in similar way to reading C, where it's literally "address of" operator.

      It's never anything like the "address of" operator in Perl. In fact, it's often used to do the opposite (dereference a code ref).

      so in that context the example line starting with ampersand would read: the address of an anonymous sub containing code/literal "color" is assigned the sub on the right hand side.

      Which goes to show your way to read the ampersand is wrong. Nothing of the kind happens.

        hi ikegami. dereferencing is literally accessing something at the address pointed by variable value. perl jargon is sigil...however perl does borrow many concepts (like referencing=~pointers nuts and bolts) from C. Maybe it's not the most useful concept (in my mindset) to bring across from C to perl with me...anyhow you can dereference a code reference in perl without the ampersand, so any argument about what ampersand actually does in perl is debatable, unless you actually see some requirement for it in compiled/assembled/running perl code. and it's not in current fashion for perl code afaik. maybe i should re-read the black book..but that's quite dated now.
        the hardest line to type correctly is: stty erase ^H

      Thanks for the replies

      I don't know how your interpretation of & as the 'address of operator' makes sense in this dereferencing situation. When you dereference a reference you have to indicate what type you produce, and the '&' signifies a subroutine. For instance:

      my $scalar_ref = \10; my $x = ${$scalar_ref}; print "$x\n"; #10 my $arr_ref = [1,2,3]; my @arr = @{$arr_ref}; print "@arr\n"; #1 2 3 my $sub_ref = sub {print "hello\n"}; &{$sub_ref}();

      In the first case, $ is used, then @, and finally & is used for the subroutine.

      The syntax I am trying to understand is:

      *{"color"} = ....

      As I understand it, the braces are dereferencing a string, which means perl treats the string as a 'symbolic reference', which in turn causes perl to go to the symbol table and look for "color". Then I think the * must instruct perl to grab the glob slot that corresponds to color--at least that seems to be the way things work when a symbolic reference is on the righthand side:

      #setup: ------- $color = 10; @color = (1,2,3); sub color {print "hello\n"}; ------- $c = ${"color"}; #grab the scalar slot for 'color' print "$c\n"; #10 @arr = @{"color"}; #grab the array slot for 'color' print "@arr\n"; #1 2 3 &{"color"}(); #grab the subroutine slot for 'color' #and execute the sub

      Sorry if this appears to be a contrived example. It is a simplification of the code on p. 158 in Intermediate Perl that uses typeglobs and symbolic references to dynamically create subroutines.

Re: typeglob/symbolic reference question
by codeacrobat (Chaplain) on Nov 04, 2010 at 07:44 UTC
    Have you considered Sub::Install?
    perl -MSub::Install=install_sub -e 'install_sub({code => sub{print "g +reen".$/}, into => +__PACKAGE__, as => "color"}); color()' green

    print+qq(\L@{[ref\&@]}@{['@'x7^'!#2/"!4']});

      Why? In the end, it does exactly the same thing:

      *{"$pkg\::$name"} = $code;

      but a hell of a lot more convolutely.

      And along the way, eschews using perfectly good Perl in favour of long laborious constructs because the author prefers to "whore for Devel::Cover"

      # I'd rather use ||= but I'm whoring for Devel::Cover. for (qw(into from)) { $arg->{$_} = $calling_pkg unless $arg->{$_} +}

      That's just ...


      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".
      In the absence of evidence, opinion is indistinguishable from prejudice.
Re: typeglob/symbolic reference question
by 7stud (Deacon) on Nov 04, 2010 at 02:31 UTC
    Hmmm...maybe the answer to my first post is simply: you can assign to a type glob, *{"color"}, but you can't assign to a subroutine, &{"color"}.

    Therefore, if you have a string and you want to dynamically define a function of the same name and then execute the function, you can:

    1) Use the string to retrieve a typeglob and then assign a subroutine to the typeglob, which serves to define the function.

    *{"do_stuf"} = sub {print "hello"};

    2) Subsequently, use the the string as a symbolic reference to retrieve the function and execute it.

    &{"do_stuff"}();