Re: Import constants as scalar instead of bareword sub
by BrowserUk (Patriarch) on Nov 03, 2005 at 02:15 UTC
|
It's (another) area where I find myself questioning the logic.
Why do Perl programmer's expect to be able to interpolate constants--into strings?
In most every other languages, you would need to interpret the value of a symbolic constant into a string using whatever their equivalent of (s)printf is. Admittedly, they also require you to do the same with variables, which Perl doesn't. But that's a convenience that Perl provides--yah Perl--not something that should be expected.
It is similarly impossible to directly interpolate a hash into a string without resorting to special measures:
perl> %hash = 1 .. 10;;
perl> print "%hash";;
%hash
perl> print "@{[ %hash ]}";;
1 2 3 4 7 8 9 10 5 6
and even then the results are pretty unsatisfactory.
And you can achieve a similar, complicated interpolation of constant subs into strings.
perl> use constant X => 12345;;
perl> print X;;
12345
perl> print "${ \ X }";;
12345
perl> print "@{[ X ]}";;
12345
perl> print 'stuff ' . X .' other stuff';;
stuff 12345 other stuff
perl> printf "stuff %s other stuff\n", X;;
stuff 12345 other stuff
Given the other benefits of constant subs:
Inlining (when instantiated at the right time :).
Readonly, without overhead.
Clearly distingished from variables without reliance upon convention.
Discarding them on the basis that they do not interpolate seems like throwing the baby out with the bathwater, when there seems to me to be a very simple and effective solution to the problem:
perl> *X = sub{ 12345 };;
perl> *X = \12345;;
perl> print X;;
12345
perl> print "${X}";;
12345
perl> print "$X";;
12345
If the constant module were modified to instantiate scalar constants as both constant subs and constant scalars with the same name, it renders constants that retain all the advantages of constant subs whilst giving the ability for them to be interpolated.
The third usage above "$X" would be deprecate in favour of the second "$(X}" that retains the visual continuity with the bareword version, and incidently allows abuttment of the constant with other text or constants without misinterpretation.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
Re: Import constants as scalar instead of bareword sub
by dragonchild (Archbishop) on Nov 02, 2005 at 18:32 UTC
|
Exporter already supports this. The problem is that this can allow the modification of the constant in a global way.
My criteria for good software:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] |
|
|
What Exporter supports? Autocopying function to scalar? Can you give me example how I can get $O_NONBLOCK using use Fcntl qw(some dirty Exporter magic);?
About modification of constants... yeah, you right. But I suppose this can be solved without performance loss by marking that scalar read-only in SV..? Anyway, I prefer read-write scalars to bareword subs. And if I'll see in somebody's code: $EINTR=123; than I will fire that guy immediatelly! :)
| [reply] [d/l] [select] |
|
|
Do you also disallow soft references? ${$foo} = 123; is just as dangerous. As for autocopying a function to a scalar, that's not hard to do with a generalized import function. Learn a little symbol table magic - it's good for the soul. :-)
My criteria for good software:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] [d/l] |
|
|
|
|
Re: Import constants as scalar instead of bareword sub
by Zaxo (Archbishop) on Nov 02, 2005 at 19:11 UTC
|
I don't understand your objection to "bareword subs". Perl understands them perfectly well if they are defined before they're called.
In practice, it's pretty rare that constants need to be interpolated, except maybe when debugging with print.
You can make $EINTR a constant with:
*EINTR = \4;
but that kind of thing is not always portable between systems. Because the value must be a reference to a literal in source, afaik that would require some kind of source filter to get portability. Not worth it, IMO.
| [reply] [d/l] |
|
|
People are constantly (pun intended) being bitten by bareword subs not getting interpolated. A classic example is when they are used as a hash key, or on the left side of the => operator. And I often need them interpolated in strings, like SQL statements, which is really ugly with subs. I never use subs as constants for this reason.
| [reply] |
|
|
You can make $EINTR a constant with:
*EINTR = \4;
but that kind of thing is not always portable between systems. Because the value must be a reference to a literal in source, afaik that would require some kind of source filter to get portability.
Out of curiousity, I'd like to understand better why you think this isn't portable? The value could also be a reference to a literal in an eval, not just the source itself. For example, here's a quick test I wrote (without the kind of robust error checking on the inputs that I'd write for a real module, of course.)
ConstantVars.pm
package ConstantVars;
use strict;
use warnings;
sub import {
my $package = shift;
die "Odd number of arguments to ConstantVars" if ! @_ % 2;
my %constants = @_;
my $caller = caller;
while (my ($key,$value) = each %constants) {
eval qq( *${caller}::${key} = \\"${value}" );
}
}
1;
test_constants.pl:
use strict;
use warnings;
use ConstantVars (
PI => 3.14159,
VERSION => 1.01,
);
print "PI is $PI in version $VERSION\n";
Prints:
PI is 3.14159 in version 1.01
-xdg
Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.
| [reply] [d/l] [select] |
|
|
Another option is the Readonly module, available on CPAN. I've never tried it, but some people recommend it.
| [reply] |
|
|
I've read about it in Perl Best Practices book and I've tried it. It looks very promising (especially with Readonly::XS helper module).
But Devel::DProf show it's still use tie()ed scalar (and so call FETCH() on each scalar access) even with Readonly::XS installed.
This was sadly news, because I feel like with Readonly::XS it should just 'mark' SV as read-only and doesn't tie() it. :-(
| [reply] [d/l] [select] |
|
|
but that kind of thing is not always portable between systems. Because the value must be a reference to a literal in source, afaik that would require some kind of source filter to get portability.
You lost me there. Can you give an example of this construct not being portable? I actually prefer this style of constants, so if there is a reason I should avoid it, I'd very much like to understand it.
-sauoq
"My two cents aren't worth a dime.";
| [reply] |
|
|
Why not portable? Why source filters? I sure it as simple as:
eval q{*O_NONBLOCK = \\}.O_NONBLOCK;
This code work, and work even a little (1-20%) faster than usual scalar! It's portable and can be executed automatically in import() if it see user try to import $O_NONBLOCK instead of O_NONBLOCK.
| [reply] [d/l] |
Re: Import constants as scalar instead of bareword sub
by Perl Mouse (Chaplain) on Nov 03, 2005 at 10:00 UTC
|
it's much faster in many cases
And much slower in other cases. Empty prototyped subs which only return a constant and are know at compile time are constant folded at compile time. Unlike variables:
$ perl -MO=Deparse -e 'sub CONST(){1;} my $CONST = 1; print CONST,
+ $CONST'
sub CONST () { 1 }
my $CONST = 1;
print 1, $CONST;
-e syntax OK
I can live with modules exporting constants as subs, and I can live with modules exporting constants as variables. But I would oppose changing core modules. Errno, Fcntl and Socket export constants as subs. No need for a change.
| [reply] [d/l] |
Re: Import constants as scalar instead of bareword sub
by johnnywang (Priest) on Nov 02, 2005 at 19:50 UTC
|
I agree it's better than barewords. I almost never use bareword constants, instead always use variables. I think capitalization is enough to remind users not to change them:
package constants;
use strict;
use Exporter;
our @ISA = qw/Exporter/;
our @EXPORT_OK= ($FOO, @BAR, %HASH);
our $FOO = 1;
our @BAR = qw/A B C/;
our %HASH = (A=>1,B=>2);
1
| [reply] [d/l] |