RFC: UniqueID Module

J

John

I need an alternative for 'use constant', but that allows the definition of
unique IDs not associated with scalar values.

I propose the UniqueID class, summarized below. I'm asking this list:

- Is this module necessary, or is there a better way to do this?
- Is it aptly named?
- Other comments before I post to CPAN?


Synopsis:

use UniqueID qw (red yellow blue);

my $color = red;

print "Just as we thought!\n" if $color == red;
print "This will never happen!\n" if $color == blue;
print "Nor will this!\n" if $color == 'red';

print "Color is ".$color->name.".\n";

$color + 1; #Raises an exception. Addition isn't defined for UniqueID
objects.

Output:
Just as we thought!
Color is red.
The '+' operation isn't defined for UniqueID objects at C:\test5.pl line 11


Rationale:

UniqueID lets you define unique identifiers that are not associated with
scalar values. Contrast this with the "constant" package, which simply
aliases names to scalar values.

UIDs are safer than constants, because they can not accidently be confused
with un-related scalars. For example, the following accident might happen
using aliased constants:

use constant red => 0;

my $color;
my $color = red if not 1; #*Don't* set $color to red.

#Comparing $color to red is the same as comparing it to 0.
if($color == red) {
print "Color is red!\n";
} else {
print "Color is NOT red!\n";
}

Output: Color is red!

Whereas colors can never be confused with numbers if you use UIDs:

use UniqueID qw (red);

my $color = red if 0; #*Don't* set $color to red.

###Red doesn't equal 0 or undef.
if($color == red) {
print "Color is red!\n";
} else {
print "Color is NOT red!\n";
}

Output: Color is NOT red!
 
A

Anno Siegel

John said:
I need an alternative for 'use constant', but that allows the definition of
unique IDs not associated with scalar values.

I propose the UniqueID class, summarized below. I'm asking this list:

- Is this module necessary, or is there a better way to do this?
- Is it aptly named?
- Other comments before I post to CPAN?


Synopsis:

use UniqueID qw (red yellow blue);

my $color = red;

print "Just as we thought!\n" if $color == red;
print "This will never happen!\n" if $color == blue;
print "Nor will this!\n" if $color == 'red';
^^
Wrong comparison operator. "==" is for numbers, you need "eq".
print "Color is ".$color->name.".\n";

$color + 1; #Raises an exception. Addition isn't defined for UniqueID
objects.

Output:
Just as we thought!
Color is red.
The '+' operation isn't defined for UniqueID objects at C:\test5.pl line 11

You can have unique identifiers in Perl by just taking a reference to
anything. So you get most of the behavior you describe after just saying:

use constant red => \ '';
use constant yellow => \ '';
use constant blue => \ '';

instead of

use UniqueID qw (red yellow blue);

The difference is that the arithmetic operation is allowed and that
there is no ->name method.
Rationale:

UniqueID lets you define unique identifiers that are not associated with
scalar values. Contrast this with the "constant" package, which simply
aliases names to scalar values.

UIDs are safer than constants, because they can not accidently be confused
with un-related scalars. For example, the following accident might happen
using aliased constants:

use constant red => 0;

my $color;
my $color = red if not 1; #*Don't* set $color to red.

#Comparing $color to red is the same as comparing it to 0.
if($color == red) {
print "Color is red!\n";
} else {
print "Color is NOT red!\n";
}

Output: Color is red!

....plus a warning, if warnings are on as they should. While an undef
*can* be mistaken for a 0 (or a ""), there is a difference, and a
program can tell which is which.

[...]

I have as yet not missed the functionality of UniqueID.

Anno
 
J

John

One more point in the case for UniqueID that I didn't mention: a UniqueID
would maintain it's identity through serialization across processes. It
would be implemented as a blessed reference to a scalar containing the
symbol name.

Also, below is another perhaps beter example of UniqueID's use, although it
is still contrived:

use UniqueID 'error';

sub bar {
my($arg) = @_;
return not(1) ? $arg : error;
}

my $foo = bar('baz');
print $foo eq error ? "Foo is no good." : "Foo is $foo.";

The actual situation where I want to use UniqueID is too complicated for
this post, but I need a variable to be able to contain *any* value that the
user can throw at it, *or* some flag value that is above all the other
values

Also, are you sure that == and != wouldn't bet appropriate comparators,
since UniqueID objects aren't strings any more than they are numbers, and
the == operators implies "mathematical identity", which is really what I'm
aiming for. So you'd have

use UniqueID 'infinity';
my $variable = 5;
print "5 is infinite!\n" if $variable == infinity;



Anno Siegel said:
John said:
I need an alternative for 'use constant', but that allows the definition of
unique IDs not associated with scalar values.

I propose the UniqueID class, summarized below. I'm asking this list:

- Is this module necessary, or is there a better way to do this?
- Is it aptly named?
- Other comments before I post to CPAN?


Synopsis:

use UniqueID qw (red yellow blue);

my $color = red;

print "Just as we thought!\n" if $color == red;
print "This will never happen!\n" if $color == blue;
print "Nor will this!\n" if $color == 'red';
^^
Wrong comparison operator. "==" is for numbers, you need "eq".
print "Color is ".$color->name.".\n";

$color + 1; #Raises an exception. Addition isn't defined for UniqueID
objects.

Output:
Just as we thought!
Color is red.
The '+' operation isn't defined for UniqueID objects at C:\test5.pl line
11

You can have unique identifiers in Perl by just taking a reference to
anything. So you get most of the behavior you describe after just saying:

use constant red => \ '';
use constant yellow => \ '';
use constant blue => \ '';

instead of

use UniqueID qw (red yellow blue);

The difference is that the arithmetic operation is allowed and that
there is no ->name method.
Rationale:

UniqueID lets you define unique identifiers that are not associated with
scalar values. Contrast this with the "constant" package, which simply
aliases names to scalar values.

UIDs are safer than constants, because they can not accidently be confused
with un-related scalars. For example, the following accident might happen
using aliased constants:

use constant red => 0;

my $color;
my $color = red if not 1; #*Don't* set $color to red.

#Comparing $color to red is the same as comparing it to 0.
if($color == red) {
print "Color is red!\n";
} else {
print "Color is NOT red!\n";
}

Output: Color is red!

...plus a warning, if warnings are on as they should. While an undef
*can* be mistaken for a 0 (or a ""), there is a difference, and a
program can tell which is which.

[...]

I have as yet not missed the functionality of UniqueID.

Anno
 
J

John

Here is a proposed implementation for the package:

use strict; use warnings;
package UniqueID;
our $VERSION = '1.01';

use Carp;

use overload
'==' => sub {my $class = ref(shift); croak "'==' operator isn't defined
for $class objects. Did you mean 'eq'?"},
'eq' => 'equals',
'!=' => sub {my $class = ref(shift); croak "'!=' operator isn't defined
for $class objects. Did you mean 'ne'?"},
'ne' => 'notequals',
nomethod => sub {
my($a, $b, $c, $operator) = @_;
my $class = ref($a);
croak "The '$operator' operation isn't defined for $class objects";
},
'""' => 'stringify'
;

sub stringify {
my($self) = @_;
return overload::StrVal($self).'='.$$self;
}
sub equals {
ref($_[1]) eq ref($_[0]) and ${$_[0]} eq ${$_[1]}
};
sub notequals {
not (ref($_[1]) eq ref($_[0]) and ${$_[0]} eq ${$_[1]})
};

sub name {
my($self) = @_;
my @parts = split /\:\:/, $$self;
return $parts[-1];
}

sub fullname {
my($self) = @_;
return $$self;
}

sub new {
my($pkg, $client_package, $name) = @_;

my $string = $client_package."::".$name;
my $self = bless \$string, $pkg;
return $self;
}

sub make_identifier {
my($pkg, $client_package, $name) = @_;

my $id = $pkg->new($client_package, $name);

no strict 'refs';
*{$client_package."::".$name} = sub {
$id;
};
}

sub import {
my($pkg, @names) = @_;
my $client_package = caller(0);
for(@names) {
$pkg->make_identifier($client_package, $_);
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top