Not quite so relicy as thought in #11651 (op/concat #4 and #5
[p5sagit/p5-mst-13.2.git] / lib / constant.pm
index 72ad793..ac37a66 100644 (file)
@@ -1,11 +1,11 @@
 package constant;
 
 use strict;
-use 5.005_64;
+use 5.006_00;
 use warnings::register;
 
 our($VERSION, %declared);
-$VERSION = '1.02';
+$VERSION = '1.04';
 
 #=======================================================================
 
@@ -28,75 +28,93 @@ my %forbidden = (%keywords, %forced_into_main);
 sub import {
     my $class = shift;
     return unless @_;                  # Ignore 'use constant;'
-    my $name = shift;
-    unless (defined $name) {
-        require Carp;
-       Carp::croak("Can't use undef as constant name");
+    my %constants = ();
+    my $multiple  = ref $_[0];
+
+    if ( $multiple ) {
+       if (ref $_[0] ne 'HASH') {
+           require Carp;
+           Carp::croak("Invalid reference type '".ref(shift)."' not 'HASH'");
+       }
+       %constants = %{+shift};
+    } else {
+       $constants{+shift} = undef;
     }
-    my $pkg = caller;
-
-    # Normal constant name
-    if ($name =~ /^_?[^\W_0-9]\w*\z/ and !$forbidden{$name}) {
-        # Everything is okay
-
-    # Name forced into main, but we're not in main. Fatal.
-    } elsif ($forced_into_main{$name} and $pkg ne 'main') {
-       require Carp;
-       Carp::croak("Constant name '$name' is forced into main::");
-
-    # Starts with double underscore. Fatal.
-    } elsif ($name =~ /^__/) {
-       require Carp;
-       Carp::croak("Constant name '$name' begins with '__'");
-
-    # Maybe the name is tolerable
-    } elsif ($name =~ /^[A-Za-z_]\w*\z/) {
-       # Then we'll warn only if you've asked for warnings
-       if (warnings::enabled()) {
-           if ($keywords{$name}) {
-               warnings::warn("Constant name '$name' is a Perl keyword");
-           } elsif ($forced_into_main{$name}) {
-               warnings::warn("Constant name '$name' is " .
-                   "forced into package main::");
+
+    foreach my $name ( keys %constants ) {
+       unless (defined $name) {
+           require Carp;
+           Carp::croak("Can't use undef as constant name");
+       }
+       my $pkg = caller;
+
+       # Normal constant name
+       if ($name =~ /^_?[^\W_0-9]\w*\z/ and !$forbidden{$name}) {
+           # Everything is okay
+
+       # Name forced into main, but we're not in main. Fatal.
+       } elsif ($forced_into_main{$name} and $pkg ne 'main') {
+           require Carp;
+           Carp::croak("Constant name '$name' is forced into main::");
+
+       # Starts with double underscore. Fatal.
+       } elsif ($name =~ /^__/) {
+           require Carp;
+           Carp::croak("Constant name '$name' begins with '__'");
+
+       # Maybe the name is tolerable
+       } elsif ($name =~ /^[A-Za-z_]\w*\z/) {
+           # Then we'll warn only if you've asked for warnings
+           if (warnings::enabled()) {
+               if ($keywords{$name}) {
+                   warnings::warn("Constant name '$name' is a Perl keyword");
+               } elsif ($forced_into_main{$name}) {
+                   warnings::warn("Constant name '$name' is " .
+                       "forced into package main::");
+               } else {
+                   # Catch-all - what did I miss? If you get this error,
+                   # please let me know what your constant's name was.
+                   # Write to <rootbeer@redcat.com>. Thanks!
+                   warnings::warn("Constant name '$name' has unknown problems");
+               }
+           }
+
+       # Looks like a boolean
+       # use constant FRED == fred;
+       } elsif ($name =~ /^[01]?\z/) {
+            require Carp;
+           if (@_) {
+               Carp::croak("Constant name '$name' is invalid");
            } else {
-               # Catch-all - what did I miss? If you get this error,
-               # please let me know what your constant's name was.
-               # Write to <rootbeer@redcat.com>. Thanks!
-               warnings::warn("Constant name '$name' has unknown problems");
+               Carp::croak("Constant name looks like boolean value");
            }
-       }
 
-    # Looks like a boolean
-    #          use constant FRED == fred;
-    } elsif ($name =~ /^[01]?\z/) {
-        require Carp;
-       if (@_) {
-           Carp::croak("Constant name '$name' is invalid");
        } else {
-           Carp::croak("Constant name looks like boolean value");
+          # Must have bad characters
+            require Carp;
+           Carp::croak("Constant name '$name' has invalid characters");
        }
 
-    } else {
-       # Must have bad characters
-        require Carp;
-       Carp::croak("Constant name '$name' has invalid characters");
-    }
-
-    {
-       no strict 'refs';
-       my $full_name = "${pkg}::$name";
-       $declared{$full_name}++;
-       if (@_ == 1) {
-           my $scalar = $_[0];
-           *$full_name = sub () { $scalar };
-       } elsif (@_) {
-           my @list = @_;
-           *$full_name = sub () { @list };
-       } else {
-           *$full_name = sub () { };
+       {
+           no strict 'refs';
+           my $full_name = "${pkg}::$name";
+           $declared{$full_name}++;
+           if ($multiple) {
+               my $scalar = $constants{$name};
+               *$full_name = sub () { $scalar };
+           } else {
+               if (@_ == 1) {
+                   my $scalar = $_[0];
+                   *$full_name = sub () { $scalar };
+               } elsif (@_) {
+                   my @list = @_;
+                   *$full_name = sub () { @list };
+               } else {
+                   *$full_name = sub () { };
+               }
+           }
        }
     }
-
 }
 
 1;
@@ -133,6 +151,17 @@ constant - Perl pragma to declare constants
     print CCODE->("me");
     print CHASH->[10];                 # compile-time error
 
+    # declaring multiple constants at once
+    use constant {
+       BUFFER_SIZE     => 4096,
+       ONE_YEAR        => 365.2425 * 24 * 60 * 60,
+       PI              => 4 * atan2( 1, 1 ),
+       DEBUGGING       => 0,
+       ORACLE          => 'oracle@cs.indiana.edu',
+       USERNAME        => scalar getpwuid($<),      # this works
+       USERINFO        => getpwuid($<),             # THIS IS A BUG!
+    };
+
 =head1 DESCRIPTION
 
 This will declare a symbol to be a constant with the given scalar
@@ -171,41 +200,84 @@ compile time.
 
 Constant symbols are package scoped (rather than block scoped, as
 C<use strict> is). That is, you can refer to a constant from package
-Other as C<Other::CONST>.
+Other as C<Other::CONST>.  You may also use constants as either class
+or object methods, ie. C<< Other->CONST() >> or C<< $obj->CONST() >>.
+Such constant methods will be inherited as usual.
 
 As with all C<use> directives, defining a constant happens at
 compile time. Thus, it's probably not correct to put a constant
 declaration inside of a conditional statement (like C<if ($foo)
-{ use constant ... }>).
+{ use constant ... }>).  When defining multiple constants, you
+cannot use the values of other constants within the same declaration
+scope.  This is because the calling package doesn't know about any
+constant within that group until I<after> the C<use> statement is
+finished.
+
+    use constant {
+       AGE    => 20,
+       PERSON => { age => AGE }, # Error!
+    };
+    [...]
+    use constant PERSON => { age => AGE }; # Right
 
-Omitting the value for a symbol gives it the value of C<undef> in
-a scalar context or the empty list, C<()>, in a list context. This
-isn't so nice as it may sound, though, because in this case you
-must either quote the symbol name, or use a big arrow, (C<=E<gt>>),
-with nothing to point to. It is probably best to declare these
-explicitly.
+Giving an empty list, C<()>, as the value for a symbol makes it return
+C<undef> in scalar context and the empty list in list context.
 
-    use constant UNICORNS      => ();
-    use constant LOGFILE       => undef;
+    use constant UNICORNS => ();
 
-The result from evaluating a list constant in a scalar context is
-not documented, and is B<not> guaranteed to be any particular value
-in the future. In particular, you should not rely upon it being
-the number of elements in the list, especially since it is not
-B<necessarily> that value in the current implementation.
+    print "Impossible!\n"  if defined UNICORNS;    
+    my @unicorns = UNICORNS;  # there are no unicorns
 
-Magical values, tied values, and references can be made into
-constants at compile time, allowing for way cool stuff like this.
-(These error numbers aren't totally portable, alas.)
+The same effect can be achieved by omitting the value and the big
+arrow entirely, but then the symbol name must be put in quotes.
+
+    use constant "UNICORNS";
+
+The result from evaluating a list constant with more than one element
+in a scalar context is not documented, and is B<not> guaranteed to be
+any particular value in the future. In particular, you should not rely
+upon it being the number of elements in the list, especially since it
+is not B<necessarily> that value in the current implementation.
+
+Magical values and references can be made into constants at compile
+time, allowing for way cool stuff like this.  (These error numbers
+aren't totally portable, alas.)
 
     use constant E2BIG => ($! = 7);
     print   E2BIG, "\n";       # something like "Arg list too long"
     print 0+E2BIG, "\n";       # "7"
 
+You can't produce a tied constant by giving a tied scalar as the
+value.  References to tied variables, however, can be used as
+constants without any problems.
+
 Dereferencing constant references incorrectly (such as using an array
 subscript on a constant hash reference, or vice versa) will be trapped at
 compile time.
 
+When declaring multiple constants, all constant values B<must be
+scalars>.  If you accidentally try to use a list with more (or less)
+than one value, every second value will be treated as a symbol name.
+
+    use constant {
+        EMPTY => (),                    # WRONG!
+        MANY => ("foo", "bar", "baz"),  # WRONG!
+    };
+
+This will get interpreted as below, which is probably not what you
+wanted.
+
+    use constant {
+        EMPTY => "MANY",  # oops.
+        foo => "bar",     # oops!
+        baz => undef,     # OOPS!
+    };
+
+This is a fundamental limitation of the way hashes are constructed in
+Perl.  The error messages produced when this happens will often be
+quite cryptic -- in the worst case there may be none at all, and
+you'll only later find that something is broken.
+
 In the rare case in which you need to discover at run time whether a
 particular constant has been declared via this module, you may use
 this function to examine the hash C<%constant::declared>. If the given
@@ -268,6 +340,12 @@ C<CONSTANT =E<gt> 'value'>.
 Tom Phoenix, E<lt>F<rootbeer@redcat.com>E<gt>, with help from
 many other folks.
 
+Multiple constant declarations at once added by Casey West,
+E<lt>F<casey@geeknest.com>E<gt>.
+
+Assorted documentation fixes by Ilmari Karonen,
+E<lt>F<perl@itz.pp.sci.fi>E<gt>.
+
 =head1 COPYRIGHT
 
 Copyright (C) 1997, 1999 Tom Phoenix