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

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

I'm attempting to write a constant::lexical pragma and the only unresolved issue as of now is that the constant names are valid barewords even though the constant subroutine has been undefined.

use 5.010; use strict; sub FOO () { 'foo' } BEGIN { undef &FOO } say defined &FOO ? 'subroutine defined' : 'subroutine undef'; say eval 'my $false; FOO if $false; 1' ? 'FOO allowed' : 'FOO not allowed' ; __END__ subroutine undef FOO allowed
This is a major issue as one of the great features of constants is that they normally give compile-time errors if not defined.

I don't see any way to do this in pure Perl. I've browsed perldata, perlapi, perlguts, and perlintern for any ideas but I haven't found anything useful. I don't grok most of the guts. Is it possible using XS? Of course, XS is the very last resort.

lodin

Update: Added stricture and my $false; in the eval to make the test actually work.

Replies are listed 'Best First'.
Re: Removing CODE slot in typeglob / Reversing "use subs ...;"
by shmem (Chancellor) on Jan 03, 2008 at 20:08 UTC
    The only way I see is saving the typeglob, destroying it and re-creating it with all but the CODE slot:
    use 5.010; use strict; our $FOO; BEGIN { $FOO = 'still foo'; } sub FOO () { 'foo' } BEGIN { *BAR = *FOO; say "in BEGIN (1): \$FOO = '$FOO'"; undef *FOO; say "in BEGIN (2): \$FOO = '$FOO'"; *FOO = *BAR{SCALAR}; undef *BAR; } say defined &FOO ? 'subroutine defined' : 'subroutine undef'; say eval 'my $false; FOO if $false; 1' ? 'FOO allowed' : 'FOO not allowed' ; say $FOO; __END__ in BEGIN (1): $FOO = 'still foo' in BEGIN (2): $FOO = '' subroutine undef FOO not allowed still foo

    update: tweaked the code to more closely resemble the OP's.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      The only way I see is saving the typeglob, destroying it and re-creating it with all but the CODE slot

      Yup, that is correct. That is how we do it in Class::MOP::Package, and when I was writing that bit at the YAPC::NA 2005 hackathon I queried the collective wisdom of the gurus in attendence (larry, audrey and chromatic to name a few) and they all agreed that was the only way. And AFAIK there is no cleaner way to do it in XS either.

      -stvn
        I wonder, is that a design accident or intended? you can assign to a typeglob slot (assigning a specific ref to that typeglob), but you cannot undef its value, you have to destroy the whole glob. You cannot assign e.g. an 'undefined subref' to a CODE slot, because... no such thing. Something undef is not a CODE ref.

        Did you get told the rationale behind that? Does anybody know? Cloning a typeglob to get rid of one entry in it looks like "useless use of hoops" to me. IMHO there should be a syntax to remove slots, keys, values and all, as delete does with plain hashes.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      The reason I didn't like this was that you couldn't access formats using *foo{THING} in Perl 5.6. I'd missed that *foo{FORMAT} now works (perl58delta). *foo{FORMAT} is not mentioned in either perldata or perlmod, but I found it in perlref. Really, that's not the first place I'd look to see which slots there are in a typeglob ...

      Thanks for forcing me to try (and fail) to proof that *foo{FORMAT} doesn't work, because that's what I originally intended to reply with. :-)

      *foo{FORMAT} and the resulting *foo{NAME} for the no-longer-constants was my only conserns, both of which are now solved. So I guess that marks the issue solved, unless you can see some other pitfall with this technique.

      lodin

Re: Removing CODE slot in typeglob / Reversing "use subs ...;"
by BrowserUk (Patriarch) on Jan 03, 2008 at 18:03 UTC

    I've done no real testing (my food is ready), but is this the effect you are looking for?

    #! perl -slw use strict; use 5.010; sub FOO () { 'foo' } BEGIN { undef $::{FOO} } say defined &FOO ? 'subroutine defined' : 'subroutine undef'; say eval 'FOO if $false; 1' ? 'FOO allowed' : 'FOO not allowed'; __END__ C:\test>660264.p10 subroutine undef FOO not allowed

    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.

      Beware of the $false in there. Your eval fails because you turned on stricture, but even with my $false; it fails, so that's good. Unfortunately it works too well.

      undef $::{FOO} is the same as undef *FOO and that undefined the whole typeglob.

      our $FOO = 'foo'; undef $::{FOO}; print defined $FOO ? 'defined' : 'undef'; __END__ undef
      I only want to remove the CODE slot in the typeglob.

      lodin

        Again, I'm only adressing the symptoms rather than thinking things through, but how's this?

        #! perl -slw use strict; use 5.010; our $FOO = 'fred'; sub FOO () { 'foo' } BEGIN { undef $::{FOO}{CODE} } say defined &FOO ? 'subroutine defined' : 'subroutine undef'; say eval 'FOO if $false; 1' ? 'FOO allowed' : 'FOO not allowed'; print $FOO; __END__ C:\test>660264.p10 subroutine defined FOO not allowed fred

        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.