views:

247

answers:

7

I have done some digging through perldoc and the O'Reilly books but haven't found any way to do this. Am I relegated to using something like Readonly?

UPDATE:

I have nothing against Readonly. I just wanted to be able to do something like PHP's constant().

Example if Perl had constant():

use constant {
  FIELD_EXAMPLE_O => 345,
  FIELD_EXAMPLE_L => 25
};

my $var = 'EXAMPLE';
my $c = 'FIELD_' . $var . '_L';
my $value = constant($c);

# $value is 25

If Readonly is the best way to do it, then I shall use it.

+7  A: 

Yes. See perldoc constant.

mobrule
A: 

Use this format:

use constant DFLT_MIN_LENGTH_PWD => 6;
David Brunelle
+1  A: 

You can use the "use constant" pragma, but constants defined that way aren't referenced like normal variables. They do get you the performance increase of having constants, though. If you're not looking for the compile-time optimizations of using a constant, your best approach is probably just using discipline when writing the code and not assigning to variables that shouldn't be assigned to. :)

dannysauer
A great many issues in Perl seem to be resolved by "Don't do that!". Works for me.
David Thornley
+14  A: 

What's wrong with Readonly?

If it's too slow, you can supplement it with Readonly:XS. But if you don't like Readonly, there's always the older constant.

use constant PI => 3.14159265;

Just remember

  1. They work like subs, so they don't interpolate without work.
  2. If you want to create multiple constants in one statement, you need to pass a hash reference.

    use constant { PI => 3.14159265
                 , E  => 2.71828183
                 };
    


From Your Example:

Judging from your example, there's no reason why a readonly hash couldn't do the same thing.

Readonly::Hash my %field_example => { L => 25, O => 345 };

Then you could use it anywhere you'd want to cobble the constant:

print "The example is $field_example{$var}\n";

OR you could do it this way:

Readonly::Hash my %field 
    => { example => { L => 25,     O => 345 }
       , name    => { L => 'Lion', O => 'ocelot' }
       };

And call it this way:

$field{$var}{L};

You can get a lot of mileage about not trying to make a language do what it has better support for doing in another way.

Cognate to PHP constant

However, if you want to do it that way, then my suggestion is that the following sub is a way of doing the same ( and avoids an eval ):

sub read_constant { 
    use Symbol qw<qualify_to_ref>;
    my $name = join( '', @_ ); # no need to concatenate before passing
    my $constant;
    # use the first that works: calling package and then "" (main)
    for my $pkg ( scalar( caller ), "" ) { 
        # get symbol reference
        my $symb_ref = qualify_to_ref( $name, $pkg );
        # get the code slot
        $constant    = *{$symb_ref}{CODE};
        last if $constant;
    }
    return unless $constant;
    # call the sub named
    return $constant->();
}

You'd call it like this:

$value = read_constant( 'FIELD_', $var, 'L' );

One last thing, is that you could even put a test in front to make sure that it is only a all cap string:

Carp::croak "Invalid constant name '$name'" if $name =~ /[^\p{UpperCase}_]/;
Axeman
Your `read_constant` is doing an awful lot of work to avoid saying `no strict 'refs'`. See my answer for a shorter version. (http://stackoverflow.com/questions/1526859/does-perl-have-something-similar-to-phps-constant/1527419#1527419)
cjm
Very thorough answer, thanks!
Chris Kloberdanz
+9  A: 

You could use constant.

use constant PI    => 4 * atan2(1, 1);
use constant DEBUG => 0;

print "Pi equals ", PI, "...\n" if DEBUG;

use constant {
    SEC   => 0,
    MIN   => 1,
    HOUR  => 2,
    MDAY  => 3,
    MON   => 4,
    YEAR  => 5,
    WDAY  => 6,
    YDAY  => 7,
    ISDST => 8,
};

use constant WEEKDAYS => qw(
    Sunday Monday Tuesday Wednesday Thursday Friday Saturday
);

print "Today is ", (WEEKDAYS)[ (localtime)[WDAY] ], ".\n";

Or you could use Readonly.

use Readonly;

# Read-only scalar
Readonly::Scalar     $sca => $initial_value;
Readonly::Scalar  my $sca => $initial_value;

# Read-only array
Readonly::Array      @arr => @values;
Readonly::Array   my @arr => @values;

# Read-only hash
Readonly::Hash       %has => (key => value, key => value, ...);
Readonly::Hash    my %has => (key => value, key => value, ...);
# or:
Readonly::Hash       %has => {key => value, key => value, ...};

# You can use the read-only variables like any regular variables:
print $sca;
$something = $sca + $arr[2];
next if $has{$some_key};

# But if you try to modify a value, your program will die:
$sca = 7;
push @arr, 'seven';
delete $has{key};
# The error message is "Modification of a read-only value attempted"
Brad Gilbert
+4  A: 

Here's the constant function you're looking for:

sub constant
{
  no strict 'refs';
  shift->();                    # Call the supplied function by name
} # end constant

Just add that to the code in your question, and it will do what you asked for. The constants created by the constant pragma are just subroutines, and it's easy to call a subroutine by name.

Here's a more advanced one that still works even if you're calling it from a different package:

sub constant
{
  my $constant = shift;
  $constant = caller() . "::$constant" unless $constant =~ /::/;
  no strict 'refs';
  $constant->();                # Call function by name
} # end constant
cjm
+1 I think none of us really understood what the OP was after.
Sinan Ünür
Brilliant! You've found one of the few good reasons to disable use strict. Even better, you do it in a confined lexical scope.
daotoad
+1  A: 

Looking at the sample code you posted, I get the sense that you might be looking for Hash::Util::lock_hash and friends:

#!/usr/bin/perl

use strict;
use warnings;

use Hash::Util qw(lock_hash);

my %constant = (
    FIELD_EXAMPLE_O => 345,
    FIELD_EXAMPLE_L => 25,
);

lock_hash %constant;

my $var = 'EXAMPLE';

print $constant{"FIELD_${var}_L"}, "\n";

Output:

C:\Temp> xuk
25
Sinan Ünür