| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package UID; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | ############################################################################################################################### | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | =head1 NAME | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | B E<8212> Create unique identifier constants | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | =cut | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | ############################################################################################################################### | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | =head1 VERSION | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | Version 0.24 (April 16, 2009) | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | =cut | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | our $VERSION="0.24"; | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 1 |  |  | 1 |  | 24542 | use strict; use warnings; use Carp; use utf8; | 
|  | 1 |  |  | 1 |  | 2 |  | 
|  | 1 |  |  | 1 |  | 37 |  | 
|  | 1 |  |  | 1 |  | 5 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 25 |  | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 76 |  | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | use UID "foo";              # define a unique ID | 
| 29 |  |  |  |  |  |  | use UID BAR=>BAZ=>QUX=>;    # define some more | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | print foo==foo;             # true | 
| 32 |  |  |  |  |  |  | print foo==BAR;             # false | 
| 33 |  |  |  |  |  |  | print foo=="foo";           # false | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | do_stuff(foo 42, BAR "bar", BAZ "foo"); | 
| 36 |  |  |  |  |  |  | # similar to do_stuff(foo=>42, BAR=>"bar", BAZ=>"foo") | 
| 37 |  |  |  |  |  |  | # except the UID foo can be unambiguously distinguished from the string "foo" | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | The C module lets you declare unique identifiers E<8212> values that you can be sure will not be coincidentally matched by some other value. | 
| 43 |  |  |  |  |  |  | The values are not "universally" unique (UUIDs/GUIDs); they are unique for a single run of your program. | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | Define the identifiers with "C | 
| 46 |  |  |  |  |  |  | "C | 
| 47 |  |  |  |  |  |  | any value equal to I must be I itself (or a copy of it).  No other UID (in the same process) will be equal to I. | 
| 48 |  |  |  |  |  |  | Cs can be compared to each other (or to other values) using either C<==> or C (or C or C, of course). | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | A typical use of C objects is to form a named pair (like C<< FOO=>42 >>), but note that the pair-comma (C<< => >>) implicitly | 
| 51 |  |  |  |  |  |  | quotes the preceding word, so C<< FOO=>42 >> really means C<< "FOO"=>42 >>, using the string C<"FOO"> rather than the UID C. | 
| 52 |  |  |  |  |  |  | However, a comma is not always needed; you can say simply C and often get the same effect as C. | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | =cut | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | #=========================================================================== | 
| 60 |  |  |  |  |  |  | # | 
| 61 |  |  |  |  |  |  | # 	UID | 
| 62 |  |  |  |  |  |  | # | 
| 63 |  |  |  |  |  |  | #=========================================================================== | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | #UIDs are scoped to the package that imports them; could assign "globals" by tracking them in this package (some hash to store the names or something?)  Is this really useful??? (other than for polluting namespaces) | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | sub import | 
| 69 |  |  |  |  |  |  | #	Create a new UID of the given name | 
| 70 |  |  |  |  |  |  | # | 
| 71 |  |  |  |  |  |  | #	We take a list of names and make subs out of them (like "use constant") | 
| 72 |  |  |  |  |  |  | #	The subs don't do anything; they just return a unique reference (object) -- | 
| 73 |  |  |  |  |  |  | #	since the ref is anonymous, it will be unique (for this process) | 
| 74 |  |  |  |  |  |  | #	Needs to happen at compile time, so Perl will parse the sub/uid names nicely | 
| 75 |  |  |  |  |  |  | #	(our own "import" routine will export our identifiers at | 
| 76 |  |  |  |  |  |  | { | 
| 77 | 4 |  |  | 4 |  | 3123 | my $class=shift;							# first arg will always be the package name | 
| 78 |  |  |  |  |  |  |  | 
| 79 | 4 |  |  |  |  | 19 | for (@_)	# each name requested | 
| 80 |  |  |  |  |  |  | { | 
| 81 | 5 | 50 | 0 |  |  | 15 | carp "WARNING: Ignoring UID '$_' because it is a ref/object, not a plain string" and next if ref $_;		# UIDs can only be made out of plain strings (valid sub names)	 ###Should we allow this since the object will get stringified? or force the user to stringify it himself explicitly? | 
| 82 | 5 | 50 | 0 |  |  | 66 | carp "WARNING: Ignoring UID '$_' because that name is already being used" and next if caller()->can($_);	# hm, should be able to override this if using "no warnings redefine"! | 
| 83 |  |  |  |  |  |  | #die rather than warn, unless the existing sub is also a UID?? | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 5 |  |  |  |  | 13 | my $name=(caller()."::$_");	                    	# fully qualified name | 
| 86 | 5 |  |  |  |  | 16 | my $ID=bless [$_, $name], $class;		        	# uniqueness: since the array-ref is lexically scoped here, we'll never get this exact ref any other way | 
| 87 | 1 |  |  | 1 |  | 182 | no strict 'refs';			                    	# because we're going to declare the sub using a name built out of a string | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 153 |  | 
| 88 | 5 | 100 |  | 55 |  | 27 | *{$name}=sub {return $ID, @_ if wantarray; croak "ERROR: attempt to use args after UID $ID which is in scalar context (perhaps you need a comma after $ID?)" if @_; return $ID}; | 
|  | 5 | 50 |  |  |  | 45 |  | 
|  | 55 |  |  |  |  | 7410 |  | 
|  | 48 |  |  |  |  | 105 |  | 
|  | 48 |  |  |  |  | 203 |  | 
| 89 |  |  |  |  |  |  | # if called in array context, return any args as well (allows us to use "UID x, y" without an extra comma); | 
| 90 |  |  |  |  |  |  | # otherwise return just the UID ref itself; if we tried to pass args when being used in scalar context, | 
| 91 |  |  |  |  |  |  | # complain, because those args would effectively be lost (list in scalar context uses only the first item) | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | # We bless our refs so we can do some useful objecty stuff: | 
| 96 | 1 |  |  | 1 |  | 7675 | use overload q(""), sub { "«$_[0][0]»" };		# stringifying will return the name so we can usefully print out our IDs in error messages, etc. | 
|  | 1 |  |  | 12 |  | 1125 |  | 
|  | 1 |  |  |  |  | 9 |  | 
|  | 12 |  |  |  |  | 8279 |  | 
| 97 | 1 |  |  | 1 |  | 92 | use overload '${}', sub {\ "«$_[0][1]»" };		# scalarly de-reffing also has the effect of stringifying, to return the fully qualified name | 
|  | 1 |  |  | 2 |  | 2 |  | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 21 |  | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 19 | 100 |  | 19 | 0 | 876 | sub compare { ref($_[0]) eq ref($_[1]) and overload::StrVal($_[0]) eq overload::StrVal($_[1]) };	# for comparing two UIDs (note that first we compare the class -- if they're different kinds of objects, they can't match; if they are, then we compare the actual "memory address" values of the underlying refs, which can only be the same if both sides are in fact the same UID | 
| 100 | 1 |  |  | 1 |  | 119 | use overload "==", \&compare;	use overload "!=", sub {not &compare}; | 
|  | 1 |  |  | 1 |  | 2 |  | 
|  | 1 |  |  | 1 |  | 5 |  | 
|  | 1 |  |  |  |  | 75 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 7 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 101 | 1 |  |  | 1 |  | 69 | use overload "eq", \&compare;	use overload "ne", sub {not &compare}; | 
|  | 1 |  |  | 1 |  | 2 |  | 
|  | 1 |  |  | 7 |  | 4 |  | 
|  | 1 |  |  |  |  | 65 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 7 |  |  |  |  | 926 |  | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 1 | 0 |  | 1 |  | 150 | use overload nomethod=> sub { croak "ERROR: cannot use the '$_[3]' operator with a UID (in: ".($_[2]?"$_[1] $_[3] $_[0][0]":"$_[0][0]() $_[3] $_[1]").")" }; | 
|  | 1 |  |  | 0 |  | 2 |  | 
|  | 1 |  |  |  |  | 7 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | ###TODO: add & or | for combining flags? | 
| 106 |  |  |  |  |  |  | ###TODO: prolly should disallow redefining BEGIN, etc.!  (plagiarise some more from use-constant) | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | =head1 EXAMPLES | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | Here is an example that uses UIDs for the names of named parameters. | 
| 113 |  |  |  |  |  |  | Let's suppose we have a function (C) that takes for its arguments a list of items to do stuff to, | 
| 114 |  |  |  |  |  |  | and an optional list of filenames to log its actions to. | 
| 115 |  |  |  |  |  |  | Using ordinary strings to name the groups of arguments would look something like this: | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | do_stuff(ITEMS=> $a, $b, $c, $d, $e, FILES=> $foo, $bar); | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | The function can go through all the args looking for our "ITEMS" and "FILES" keywords. | 
| 120 |  |  |  |  |  |  | However, if one of the items happened to be the string "FILES", the function would get confused. | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | We could do something such as make the arguments take the form of a hash of array-refs | 
| 123 |  |  |  |  |  |  | (a perfectly good solution, albeit one that requires more punctuation). | 
| 124 |  |  |  |  |  |  | Or we could use UIDs (which actually allows for slightly less punctuation): | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | use UID qw/ITEMS FILES/; | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | do_stuff(ITEMS $a, $b, $c, $d, $e, FILES $foo, $bar); | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | Now the function can check for the UID C unambiguously; no string or other object will match it. | 
| 131 |  |  |  |  |  |  | Of course, you can still use I where it doesn't make sense (e.g., saying C; | 
| 132 |  |  |  |  |  |  | but you can't make something else that is intended to be different but that accidentally turns out to be equal to I. | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | =head1 TECHNICALITIES | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | Cs work by defining a subroutine of the given name in the caller's namespace. | 
| 138 |  |  |  |  |  |  | The sub simply returns a UID object. | 
| 139 |  |  |  |  |  |  | Any arguments that you feed to this sub are returned as well, which is why you can say C without a comma to separate the terms; | 
| 140 |  |  |  |  |  |  | that expression simply returns the list C<(FOO, $bar)>. | 
| 141 |  |  |  |  |  |  | (However, beware of imposing list context where it's not wanted: C puts C<$bar> in list context, as opposed to C. | 
| 142 |  |  |  |  |  |  | Also, if you are passing UIDs as arguments to a function that has a prototype, a scalar prototype (C<$>) | 
| 143 |  |  |  |  |  |  | can force the UID to return only itself, and a subsequent arg will need to be separated with a comma.) | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | These subroutines work very much as do the constants you get from C | 
| 146 |  |  |  |  |  |  | Of course, this means that the names chosen must be valid symbols (actually, you can call things almost anything in Perl, | 
| 147 |  |  |  |  |  |  | if you're prepared to refer to them using circumlocutions like C<&{"a bizarre\nname"}>!). | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | A UID overloads stringification to return a value consisting of its name when used as a string | 
| 150 |  |  |  |  |  |  | (so C | 
| 151 |  |  |  |  |  |  | You can also treat it as a scalar-reference to get a string with the fully-qualified name | 
| 152 |  |  |  |  |  |  | (that is, including the name of the package in which it lives: C). | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | The comparison operators C<==> and C and their negations are also overloaded for UID objects: | 
| 155 |  |  |  |  |  |  | comparing a UID to anything will return false unless both sides are UIDs; | 
| 156 |  |  |  |  |  |  | and if both are, their blessed references are compared. | 
| 157 |  |  |  |  |  |  | (Not the values the references are referring to, which are simply the UIDs' names, but rather the string-values of the refs, | 
| 158 |  |  |  |  |  |  | which are based on their locations in memory E<8212> | 
| 159 |  |  |  |  |  |  | since different references will always have different values, this guarantees uniqueness.) | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =head1 ERROR MESSAGES | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | =over 1 | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =item WARNING: Ignoring UID '$_' because it is a ref/object, not a plain string | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | You tried to make a UID out of something like an array-ref or an object. | 
| 169 |  |  |  |  |  |  | The module is looking for a string or strings that it can define in your namespace, and will skip over this arg. | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =item WARNING: Ignoring UID '$_' because that name is already being used | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | A subroutine (or constant, or other UID, or anything else that really is also a sub) | 
| 175 |  |  |  |  |  |  | has already been declared with the given name. | 
| 176 |  |  |  |  |  |  | UID prevents you from redefining that name and skips over it. | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =item ERROR: attempt to use args after UID $_ which is in scalar context (perhaps you need a comma after $_?) | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | 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 | 
| 182 |  |  |  |  |  |  | (not the UID plus its arguments).  The solution is probably to put a comma after the UID, or strategically place some parentheses, | 
| 183 |  |  |  |  |  |  | to separate it from the following item, rather than letting it take that item as an argument. | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =item ERROR: cannot use the '$_' operator with a UID | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | You tried to operate on a UID with an operator that doesn't apply (which is pretty much all of them). | 
| 188 |  |  |  |  |  |  | UIDs can be compared with C<==> or C, but you can't add, subtract, divide, xor them, etc. | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | =back | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | =head1 BUGS & OTHER ANNOYANCES | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | No particular bugs are known at the moment.  Please report any problems or other feedback | 
| 196 |  |  |  |  |  |  | to C<<  >>, or through the web interface at L. | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | Note that UIDs are less useful for hash keys, because the keys have to be strings, not objects. | 
| 199 |  |  |  |  |  |  | You are able to use a UID as a key, but the stringified value (its name) will actually be used (and could conceivably | 
| 200 |  |  |  |  |  |  | be accidentally duplicated). | 
| 201 |  |  |  |  |  |  | However, there are modules that can give you hash-like behaviour while allowing objects as keys, | 
| 202 |  |  |  |  |  |  | such as L or L or L. | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | 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. | 
| 205 |  |  |  |  |  |  | Sometimes you need to say things like C<+FOO> or C to make sure C is evaluated as a UID and not as a string literal. | 
| 206 |  |  |  |  |  |  | As mentioned, hash keys are one such situation; also C<< => >> implicitly quotes the preceding word. | 
| 207 |  |  |  |  |  |  | Note that C<&FOO> will work to force the sub interpretation, but is actually shorthand for C<&FOO(@_)>, | 
| 208 |  |  |  |  |  |  | i.e. it re-passes the caller's C<@_>, which is probably not what you want. | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | Comparing a UID to something else (C) will correctly return true only if the C<$something> is | 
| 211 |  |  |  |  |  |  | indeed (a copy of) the C object; but comparing something to a UID (C<$something==FOO>) could return an unexpected result. | 
| 212 |  |  |  |  |  |  | This is because of the way Perl works with overloaded operators: the value on the left gets to decide the meaning of C<==> (or C). | 
| 213 |  |  |  |  |  |  | Thus putting the UID first will check for UID-equality; if some other object comes first, it could manhandle the UID and compare, | 
| 214 |  |  |  |  |  |  | say, its string value instead. | 
| 215 |  |  |  |  |  |  | (It probably will work anyway, if the other code is well-behaved, but you should be aware of the possibility.) | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | =cut | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | ### Example of tricky object that deliberately confounds our UIDs: | 
| 220 |  |  |  |  |  |  | # 	use UID foo; | 
| 221 |  |  |  |  |  |  | # 	use overload q(==), sub {${$_[0]} eq ${$_[1]}}; use overload fallback=>1; | 
| 222 |  |  |  |  |  |  | # 	my $x="«main::foo»"; 	my $o=bless \$x; | 
| 223 |  |  |  |  |  |  | # | 
| 224 |  |  |  |  |  |  | # 	print foo==$o?"Y":"N";		# uses UID's comparison, correctly says no | 
| 225 |  |  |  |  |  |  | # 	print $o==&foo ?"Y":"N"; 	# uses cheater's comparison, says yes! | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | =pod | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | While C is slightly cleaner than C or C<< FOO=>$stuff >> [which would be an auto-quoted bareword anyway], | 
| 230 |  |  |  |  |  |  | remember that C is actually implemented as a function call taking C<$a> and C<$b> as arguments; | 
| 231 |  |  |  |  |  |  | thus it imposes list context on them.  Most of the time this doesn't matter, | 
| 232 |  |  |  |  |  |  | but if the item coming after a UID needs to be in scalar context, you may need to say something like C or C. | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | The user should have more control over the warnings and errors that C spits out. | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | =head1 COLOPHONICS | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | Copyright 2007 David Green, C<< t cpan.org> >> | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | Thanks to Tom Phoenix and others who contributed to C | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | This module is free software; you may redistribute it or modify it under the same terms as Perl itself. See L. | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | =cut | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | AYPWIP: "I think so, Brain, but then my name would be 'Thumby'!" |