62

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?

Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129
cowgod
  • 8,626
  • 5
  • 40
  • 57
  • You should always remember that PBP is advisory - the book itself as much. You need to interpret the guidelines rather than slavishly adopting them. – hexten Jun 24 '09 at 12:33

6 Answers6

46

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";
titanofold
  • 2,852
  • 1
  • 15
  • 21
Ronald Blaschke
  • 4,036
  • 2
  • 22
  • 16
  • For strictness, there is sub HOME () {'home'} made behind scene. – Hynek -Pichi- Vychodil Jan 26 '09 at 13:21
  • 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... – Cheok Yan Cheng Jun 25 '09 at 10:13
  • 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? – Phill Pafford Jan 18 '10 at 18:18
  • 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'. – Ronald Blaschke Jan 19 '10 at 11:36
  • This is actually inaccurate as later version of Perl do. Look here http://stackoverflow.com/questions/473666/does-perl-have-an-enumeration-type/1037736#1037736 – Nathan Fellman Jan 26 '10 at 07:32
  • Not that it really matters, but I would seriously consider moving the green checkmark to Nathan's answer. I just installed the enum module and it works perfectly. – Yevgeny Simkin Jun 12 '11 at 00:40
  • I still prefer this answer because it works out-of-the-box and in my opinion, still very simple. Just to add that enumeration would normally use integers to represent the different values, e.g. HOME => 0. I wouldn't use strings unless I need to actually print the names. – Nagev Aug 03 '17 at 09:52
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.

serv-inc
  • 35,772
  • 9
  • 166
  • 188
Nathan Fellman
  • 122,701
  • 101
  • 260
  • 319
  • 13
    Inasmuch as I can tell, `use enum` is a CPAN module, not part of the core language. – Paul Nathan Jan 29 '10 at 20:34
  • 2
    @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. – Yevgeny Simkin Jun 11 '11 at 22:58
  • 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!! – Brad Apr 12 '13 at 17:47
11

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

https://metacpan.org/pod/enum

titanofold
  • 2,852
  • 1
  • 15
  • 21
Ray Booysen
  • 28,894
  • 13
  • 84
  • 111
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.)

jrockway
  • 42,082
  • 9
  • 61
  • 86
4

I'm afraid perl is completely paraplegic 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!

John Roberts
  • 65
  • 1
  • 1
  • 2
    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...) – derobert Mar 06 '14 at 22:53
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].

kbro
  • 4,754
  • 7
  • 29
  • 40