Narrowly destricted refs

*{; no strict 'refs'; \*{ "${pkg}::type" } } = sub () { $type };
push @{; no strict 'refs'; \@{ "${pkg}::ISA" } }, __PACKAGE__;
# ... and so on

I really don’t feel like I have anything to add but I suppose it may not be obvious that the point of this exercise is to surgically limit the lifting of the refs stricture to just the desired symbolic dereference (without leaking it even as far as any other part of the expression) – in the most compact form possible.

I also suppose I ought to expand on it by way of explanation for the less travelled in the dustier corners of Perl 5 syntax:

The only even interesting part here is the little-realised fact that the curly braces in the traditional dereference syntax actually literally denote a block – complete with its own scope – much as curly braces do in general. Or in other words: every ${ ... } (or the like) is a do { ... } block. Just one that dereferences the value of the last expression in the block before returning it, instead of returning it verbatim (like a do { ... } block would).

The other slightly unusual part is the stray semicolon after the opening curly. That has to be there because of the ambiguity of curly braces in Perl 5 syntax: they denote blocks – or they denote some kinds of expression (e.g. hash constructors). This ambiguity is usually resolvable by looking at the inside of the curlies – but that would require the parser to speculatively parse arbitrarily far ahead, and it doesn’t operate that way. So instead it just looks a short distance ahead and then takes a guess. For 99.7% of simple cases that’s enough, so you might go a whole Perl programmer career without noticing this shortcoming. In the other cases, it keeps going until it runs into something to contradict its guess, whereupon it simply falls over. To prevent this you have to steer it in the right direction by making the curly brace unambiguous early on. If the content is a block, the simplest way of signalling that is with a semicolon.

Or to put the first line another way:

# take 1:
my $glob;
{ no strict 'refs'; $glob = \*{ "${pkg}::type" } }
*$glob = sub () { $type };

# take 2:
my $glob = do { no strict 'refs'; \*{ "${pkg}::type" } };
*$glob = sub () { $type };

# take 3:
*{; no strict 'refs'; \*{ "${pkg}::type" } } = sub () { $type };

4 Comments

Would be pretty sweet if do blocks could be an lvalue.

do { no strict 'refs'; *{ "${pkg}::type" } } = sub () { $type };

Interestingly, do blocks do work as an lvalue if they are the final statement in an lvalue sub.

Leave a comment

About Aristotle

user-pic Waxing philosophical