63

Does Perl have an enumeration type that adheres to best practices, or maybe more importantly, does it need one?

The project I am working one uses strings all over the place to denote things that would typically use an Enum in a language like C#. For example, we have a set of phone numbers in an array of hashes, each associated with a phone type ("Home", "Work", "Mobile", etc.):

$phone_number->{type} = 'Home';

Would it be sufficient to use a read-only set of variables here or should an Enum be used? I've found an enum module on CPAN but it appears to use bare words which violates one of the Perl Best Practices. My thinking on using read-only variables goes something like this:

use Readonly;

Readonly my $HOME   => 'Home';
Readonly my $WORK   => 'Work';
Readonly my $MOBILE => 'Mobile';

$phone_number->{type} = $HOME;

Is this a good approach or is there a better way?

1
  • You should always remember that PBP is advisory - the book itself as much. You need to interpret the guidelines rather than slavishly adopting them. Commented Jun 24, 2009 at 12:33

6 Answers 6

47

No, there isn't a built-in enum construct. Perl doesn't do a lot of strict typing, so I think there's actually little need for one.

In my opinion, the Readonly approach you used is solid.

There's also the more traditional constant pragma.

use constant {
    HOME   => 'Home',
    WORK   => 'Work',
    MOBILE => 'Mobile',
};

$phone_number->{type} = HOME;

Behind the scenes, it sets up a function for each constant that returns the value, like so.

sub HOME () { 'Home' }

I'd stick with Readonly unless you want to take advantage of that property, for example:

package Phone::Type;

use constant {
    HOME => 'Home',
    #...
};

package main;

print Phone::Type->HOME, "\n";
Sign up to request clarification or add additional context in comments.

7 Comments

For strictness, there is sub HOME () {'home'} made behind scene.
When you have a subroutine which take in Phone::Type enum as argument, how do you perform dynamic checking on the subroutine argument is Phone::Type type enum, not string, not number...
Just for clarification sub HOME () {'home'} and use constant { HOME => 'Home', WORK => 'Work', MOBILE => 'Mobile', }; are the same? the later generates (Behind the scene) the code?
Your examples don't match up ;-), but yes, the current implementation does. See section TECHNICAL NOTES in perldoc constant. constant also does some name checking. For example, starting with double underscores is disallowed, and names like 'BEGIN' cause a warning (with 'use warnings') because they might be confusing. For details have a look at the implementation by calling 'perldoc -m constant'.
This is actually inaccurate as later version of Perl do. Look here stackoverflow.com/questions/473666/…
|
29

Perl does in fact have an enum type like in C. Try this for details.

perldoc enum

For instance:

use enum qw(HOME WORK MOBILE);

Now we have:

HOME == 0
WORK == 1
MOBILE == 2

You can also set the indices yourself:

use enum qw(HOME=0 WORK MOBILE=10 FAX);

Now we have:

HOME == 0
WORK == 1
MOBILE == 10
FAX == 11

Look here for more details.

Note that this isn't supported in every version of Perl. I know that v5.8.3 doesn't support it, while v5.8.7 does.

6 Comments

Inasmuch as I can tell, use enum is a CPAN module, not part of the core language.
@paul nathan... I'm not sure that that should preclude someone from using it though. A huge number of perl features are not part of the core.
All the other techniques were a pain for me. I had a ton of variables, and had to label each one sequential. THEN - my outside app changed their format, requiring one definition to be added to the BEGINNING of the list - thus I had to renumber everything! enum module definitely was the way to go!!
@DevSolar I love "blast from the past" comments. I haven't written any PERL in so long that I'm having a hard time even recalling what a CPAN module is!
@YevgenySimkin, are you telling me you don't remember what you did 12 years ago? :-P
|
11

Perl doesn't support the concept natively but there are modules to add this functionality

https://metacpan.org/pod/enum

Comments

8

Your way is more than adequate.

You can also create enums with Moose::Util::TypeConstraints, if you happen to be using Moose. (Which you should be.)

use Moose::Util::TypeConstraints;

has 'phone_number' => (
    is  => 'ro',
    isa => enum([qw/ Home Work Mobile /]),
);

1 Comment

Extremely useful blog post for those who would like to dive deeper into Moose enumerations.
4

I'm afraid perl is completely inadequate when it comes to these enums and named constants:

  • enum and Readonly are not core modules (at least at perl 5.8, which I just checked). So these may be unavailable on a system where one has no freedom to install new modules.

  • "use Constant" with "use strict" (which one should always use) is completely unusable, as it generates a load of horrible error messages of the form:

    Bareword "FRED" not allowed while "strict subs" in use

Let's hope they've sorted out this mess in perl 6, if that ever sees the light of day!

2 Comments

Constants don't print warnings, and didn't in 5.8 either: perl -e 'use strict qw(subs); use constant FRED => q{fred}; printf qq{Perl %vd\nConstant is %s\n}, $^V, FRED' gives Perl 5.8.4 Constant is fred and no warnings. (Yes, I realize this answer is several years old...)
well, there is no pragmatic called Constant, which might be a problem, but if you showed your code, we might be able to find your problem.
0

This worked nicely for me...

use constant MYENUM => qw(ZERO ONE TWO THREE FOUR);

BEGIN {
    eval "use constant (MYENUM)[$_] => $_;" foreach 0..(MYENUM)-1;
}

You can then use ZERO, ONE, TWO etc as constants and print out their symbolic name using (MYENUM)[$value].

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.