3

Building off Does Perl have an enumeration type?, how can I perform dynamic type checking (or static type checking if use strict is able to do so) that my subroutine argument is getting the right type of enum?

package Phone::Type;

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

package main;

sub fun
{
    my ($my_phone_type_enum) = @_;
    # How to check my_phone_type_enum, is either Phone::Type->HOME or Phone::Type->WORK or ... but not 'Dog' or 'Cat'?
}

fun(Phone::Type->HOME); # valid
fun(Phone::Type->WORK); # valid
fun('DOG');             # run-time or compile time error
Community
  • 1
  • 1
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

3 Answers3

4

Here is one way:

#!/usr/bin/perl

package Phone::Type;

use strict;
use warnings;

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

package main;

use strict;
use warnings;

sub fun {
    my ($phone_type) = @_;
    Phone::Type->can( $phone_type )
        or die "'$phone_type' is not valid\n";
}

fun('HOME'); # valid
fun('WORK'); # valid
fun('DOG');  # run-time or compile time error
__END__

C:\Temp> dfg
'DOG' is not valid
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • But there you are passing the name of the type, not the value. That's not what was asked for. – ysth Jun 25 '09 at 15:58
  • By using use constant { HOME => 'HOME', WORK => 'WORK', }; It work very fine with this good looking syntax : fun(Phone::Type::HOME); However, when I use the Readonly. Readonly our $HOME => 'HOME'; Readonly our $WORK => 'WORK'; and I try to call with the following syntax : fun($Phone::Type::HOME); I get 'HOME' is not valid How can I make it workable too for Readonly? – Cheok Yan Cheng Jun 25 '09 at 16:46
  • p/s Some note to be added, I wish to make it workable too for Readonly, is because rest of my legacy code are using Readonly. For consistency purpose. – Cheok Yan Cheng Jun 25 '09 at 16:48
3

I would suggest that you use Readonly (as suggested in the referenced question) rather than constant. I'd suggest on of two possible approaches (depending on if you are using Perl 5.10 or 5.8).

Initially, the same code:

use strict;
use warnings;
use Readonly;

Readonly my @phone_types = qw/HOME WORK/;

Perl 5.10:

sub fun
{
   my $type = shift;
   die "Invalid phone type: $type" unless $type ~~ @phone_types;
   # ...
}

Perl 5.8:

sub fun
{
   my $type = shift;
   die "Invalid phone type: $type" unless grep { $_ eq $type} @phone_types;
   # ...
}

There is a module on CPAN which will allow you to have a great deal of control over argument types and values but I can't for the life of me remember it. Perhaps someone else can.

Nic Gibson
  • 7,051
  • 4
  • 31
  • 40
  • 1
    There's at least Params::Validate and the newer MooseX::Types. They do greatly different things, though. – tsee Jun 25 '09 at 12:19
  • 1
    Params::Validate was the one I was trying to remember. Thanks :) Probably more than a little excessive for this sort of usage though. – Nic Gibson Jun 25 '09 at 12:54
2
package Phone::Type;

my $types;
BEGIN {
    $types = {
        HOME => 'Home',
        WORK => 'Work',
    };
}
use constant $types;

sub is_phone_type {
    my ($type) = @_;
    return exists $types->{$type};
}

package main;
use Carp ();

sub fun
{
    my ($my_phone_type_enum) = @_;
    Phone::Type::is_phone_type( $my_phone_type_enum)
        or Carp::croak "Invalid type $my_phone_type_enum";
}

fun(Phone::Type->HOME); # valid
fun(Phone::Type->WORK); # valid
fun('DOG');             # run-time or compile time error

(The BEGIN is necessary to set $types at compile time so that it's available to the use statement.)

It's fairly common in Perl to be relaxed about things like this; to just assume that functions are passed numbers where you expect numbers, etc. But if you want to do this kind of validation, you may be interested in Params::Validate.

ysth
  • 96,171
  • 6
  • 121
  • 214
  • I'd put an eval in is_phone_type in case it gets passed garbage that isn't a hash ref. The answer still comes back as false. – brian d foy Jun 25 '09 at 16:11
  • @brian d foy: ? is_phone_type stringizes its input, not dereferences it. – ysth Jun 25 '09 at 16:26