NAME

UID — Create unique identifier constants

VERSION

Version 0.24 (April 16, 2009)

SYNOPSIS

  use UID "foo";              # define a unique ID
  use UID BAR=>BAZ=>QUX=>;    # define some more
  
  print foo==foo;             # true
  print foo==BAR;             # false
  print foo=="foo";           # false
  
  do_stuff(foo 42, BAR "bar", BAZ "foo");
  # similar to do_stuff(foo=>42, BAR=>"bar", BAZ=>"foo")
  # except the UID foo can be unambiguously distinguished from the string "foo"

DESCRIPTION

The UID module lets you declare unique identifiers — values that you can be sure will not be coincidentally matched by some other value. The values are not "universally" unique (UUIDs/GUIDs); they are unique for a single run of your program.

Define the identifiers with "use UID" followed by one or more strings for the names of the IDs to be created. "use UID foo" will create a unique constant called foo that is a UID object: any value equal to foo must be foo itself (or a copy of it). No other UID (in the same process) will be equal to foo. UIDs can be compared to each other (or to other values) using either == or eq (or != or ne, of course).

A typical use of UID objects is to form a named pair (like FOO=>42), but note that the pair-comma (=>) implicitly quotes the preceding word, so FOO=>42 really means "FOO"=>42, using the string "FOO" rather than the UID FOO. However, a comma is not always needed; you can say simply FOO 42 and often get the same effect as FOO, 42.

EXAMPLES

Here is an example that uses UIDs for the names of named parameters. Let's suppose we have a function (do_stuff) that takes for its arguments a list of items to do stuff to, and an optional list of filenames to log its actions to. Using ordinary strings to name the groups of arguments would look something like this:

  do_stuff(ITEMS=> $a, $b, $c, $d, $e, FILES=> $foo, $bar);

The function can go through all the args looking for our "ITEMS" and "FILES" keywords. However, if one of the items happened to be the string "FILES", the function would get confused.

We could do something such as make the arguments take the form of a hash of array-refs (a perfectly good solution, albeit one that requires more punctuation). Or we could use UIDs (which actually allows for slightly less punctuation):

  use UID qw/ITEMS FILES/;
  
  do_stuff(ITEMS $a, $b, $c, $d, $e, FILES $foo, $bar);

Now the function can check for the UID FILES unambiguously; no string or other object will match it. Of course, you can still use FILES where it doesn't make sense (e.g., saying do_stuff(ITEMS $a, FILES, $c, $d, FILES $foo, $bar); but you can't make something else that is intended to be different but that accidentally turns out to be equal to FOO.

TECHNICALITIES

UIDs work by defining a subroutine of the given name in the caller's namespace. The sub simply returns a UID object. Any arguments that you feed to this sub are returned as well, which is why you can say FOO $bar without a comma to separate the terms; that expression simply returns the list (FOO, $bar). (However, beware of imposing list context where it's not wanted: FOO $bar puts $bar in list context, as opposed to FOO, $bar. Also, if you are passing UIDs as arguments to a function that has a prototype, a scalar prototype ($) can force the UID to return only itself, and a subsequent arg will need to be separated with a comma.)

These subroutines work very much as do the constants you get from use constant. Of course, this means that the names chosen must be valid symbols (actually, you can call things almost anything in Perl, if you're prepared to refer to them using circumlocutions like &{"a bizarre\nname"}!).

A UID overloads stringification to return a value consisting of its name when used as a string (so use UID foo; print foo will display "«foo»"). You can also treat it as a scalar-reference to get a string with the fully-qualified name (that is, including the name of the package in which it lives: print ${+foo} # e.g. "«main::foo»").

The comparison operators == and eq and their negations are also overloaded for UID objects: comparing a UID to anything will return false unless both sides are UIDs; and if both are, their blessed references are compared. (Not the values the references are referring to, which are simply the UIDs' names, but rather the string-values of the refs, which are based on their locations in memory — since different references will always have different values, this guarantees uniqueness.)

ERROR MESSAGES

WARNING: Ignoring UID '$_' because it is a ref/object, not a plain string

You tried to make a UID out of something like an array-ref or an object. The module is looking for a string or strings that it can define in your namespace, and will skip over this arg.

WARNING: Ignoring UID '$_' because that name is already being used

A subroutine (or constant, or other UID, or anything else that really is also a sub) has already been declared with the given name. UID prevents you from redefining that name and skips over it.

ERROR: attempt to use args after UID $_ which is in scalar context (perhaps you need a comma after $_?)

You put (what appear to be) arguments after a UID, but the UID is in scalar context, thus only a single value can be used (not the UID plus its arguments). The solution is probably to put a comma after the UID, or strategically place some parentheses, to separate it from the following item, rather than letting it take that item as an argument.

ERROR: cannot use the '$_' operator with a UID

You tried to operate on a UID with an operator that doesn't apply (which is pretty much all of them). UIDs can be compared with == or eq, but you can't add, subtract, divide, xor them, etc.

BUGS & OTHER ANNOYANCES

No particular bugs are known at the moment. Please report any problems or other feedback to <bug-uid at rt.cpan.org>, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=UID.

Note that UIDs are less useful for hash keys, because the keys have to be strings, not objects. You are able to use a UID as a key, but the stringified value (its name) will actually be used (and could conceivably be accidentally duplicated). However, there are modules that can give you hash-like behaviour while allowing objects as keys, such as Tie::RefHash or Tie::Hash::Array or Array::AsHash.

There are other places where Perl will want to interpret a UID (like any other sub name) as a string rather than as a function call. Sometimes you need to say things like +FOO or FOO() to make sure FOO is evaluated as a UID and not as a string literal. As mentioned, hash keys are one such situation; also => implicitly quotes the preceding word. Note that &FOO will work to force the sub interpretation, but is actually shorthand for &FOO(@_), i.e. it re-passes the caller's @_, which is probably not what you want.

Comparing a UID to something else (FOO==$something) will correctly return true only if the $something is indeed (a copy of) the FOO object; but comparing something to a UID ($something==FOO) could return an unexpected result. This is because of the way Perl works with overloaded operators: the value on the left gets to decide the meaning of == (or eq). Thus putting the UID first will check for UID-equality; if some other object comes first, it could manhandle the UID and compare, say, its string value instead. (It probably will work anyway, if the other code is well-behaved, but you should be aware of the possibility.)

While FOO $stuff is slightly cleaner than FOO($stuff) or FOO=>$stuff [which would be an auto-quoted bareword anyway], remember that FOO $a, $b is actually implemented as a function call taking $a and $b as arguments; thus it imposes list context on them. Most of the time this doesn't matter, but if the item coming after a UID needs to be in scalar context, you may need to say something like FOO, $stuff or FOO scalar $stuff.

The user should have more control over the warnings and errors that UID.pm spits out.

COLOPHONICS

Copyright 2007 David Green, <plato at cpan.org>

Thanks to Tom Phoenix and others who contributed to use constant.

This module is free software; you may redistribute it or modify it under the same terms as Perl itself. See perlartistic.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 150:

Non-ASCII character seen before =encoding in '"C<«foo»>").'. Assuming UTF-8