Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass a reference of an array defined as constant?

Tags:

constants

perl

I defined hash and array constants, When passing those to a function, I'll have to pass them as references. However I'm wondering what the correct syntax is.

Consider this example:

#!/usr/bin/perl
use strict;
use warnings;

use constant AC => qw(a b c);

sub f($)
{
    print "ref=", ref $_[0], "\n";
    print "$_\n" foreach (@{$_[0]});
}

f(\AC);

When I run it I get:

ref=SCALAR
Use of uninitialized value $_ in concatenation (.) or string at /run/media/whatever/constref.pl line 10.

The Perl debugger prints AC as an array:

13: f(\AC);
  DB<1> x AC
0  'a'
1  'b'
2  'c'
  DB<2> c
like image 711
U. Windl Avatar asked Jan 26 '26 14:01

U. Windl


1 Answers

The List Constants section in the constant pragma docs tells us that

Constants may be lists of more (or less) than one value.
...
List constants are lists, not arrays.

This means, among other properties, that one cannot take a reference of that "list constant" as if it were a single entity, like an array variable is; it behaves as a list, a group of scalars.

In order to accomplish what is asked then we need to build an (anonymous) array reference out of that list and pass that, f([AC])

use warnings;
use strict;
use feature 'say';

use constant AC => qw(a b c);

sub f {
    my ($r) = @_;
    say "ref=", ref $r;
    say for @$r;
}

f( [ AC ] );

This passes the "list constant" as a single value, an array reference, and it prints as expected. However, I don't like having to copy values, nor to further lose any semblance of constant-ness. There are other ways to do this but those are even less palatable to me.§

I'd suggest to reconsider the tool to use when proper read-only variables are needed.

There are other libraries for this and I'd recommend Const::Fast, or Readonly.

use Const::Fast;    
const my @const_ary => qw(a b c);
f( \@const_ary );                 # same f() from above

use Readonly;
Readonly my @carr => qw(a b c);
f( \@carr );                      # same f() from above

These are lexical variables that one can handle like any other. See docs.


Attempting to formally "take a reference" of a list results in a list of references

\($v, $t)  -->  \$v, \$t

While the AC itself is a constant, the list that it is associated with isn't read-only

use constant AC => qw(a b c);

(AC)[1] = "other";

say for AC;

prints

a
other
c

They're just not constant.


§ I can see two other ways

  • The constant pragma produces (is implemented as) a subroutine. Then one could use that and pass it as such, f(\&AC), and then use it as such, say for $r->().

    However, now we have to pass and dereference a subroutine off of that list symbol (AC), and get a list. This is a really bad hack.

  • The code in the question uses a "constant list." One can use a reference instead and that can be passed as such

    use constant AC => [ qw(a b c) ];
    
    # same sub f { } as above
    
    f( AC );  # prints as expected
    

    However, I don't see how to dereference AC to get the whole list (@{ AC } doesn't go?), apart from copying it to an arrayref first, like in f(). But then that defies the purpose of having it as a constant -- and all pretense to constant-ness is dropped.

like image 69
zdim Avatar answered Jan 28 '26 03:01

zdim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!