I was doing a lot of foo() if ref($thing) eq 'ARRAY'
or foo() if ref($thing) eq 'HASH'
(even something idiotic like foo() if ref($thing) && "$thing" =~ m{.+?=HASH(0x.+?)}
) checks inside a pet project of mine. So thought about making it in an elegant way instead. I mean ref($thing)->array
or even ref($thing)->is_array
seemed much better as a syntactic sugar. However ref()
is clearly not the way to go for the implementation as it fails to identify the underlying type of objects. So, the obvious choice is Scalar::Util::reftype
. I've named the module Scalar::Util::Reftype. In a way, I can say that it's similar to File::stat
. Btw, Scalar::Util::reftype
has one oddity: unlike CORE::ref
it returns undef if you pass a non-ref parameter to it. So, instead of foo() if reftype($thing) eq 'ARRAY'
one must say foo() if defined reftype($thing) && reftype($thing) eq 'ARRAY'
or just foo() if reftype($thing) && reftype($thing) eq 'ARRAY'
to prevent an annoying warning:
C:\>perl -MScalar::Util=reftype -wle "my $x; print reftype($x) eq 'ARRAY'"
Use of uninitialized value in string eq at -e line 1.
It also can not detect Regexp
(CORE::ref
can, as long as it's not blessed):
C:\>perl -MScalar::Util=reftype -wle "my $x = qr//; print reftype $x"
SCALAR
C:\>perl -wle "my $x = qr//; print ref $x"
Regexp
C:\>
Fortunately, perl 5.10 comes with re::is_regexp
to detect if an object is based on a regex or not. But what about older perls? We can remedy the situation with the help of Data::Dump::Streamer::regex under at least perl 5.8.x. Unfortunately Data::Dump::Streamer seems to fail under anything older than that. I wasn't aware that re::is_regexp
is a new functionality until reached "ref() and Regexp" discussion on PerlMonks.
I also checked ref
documentation. As of perl 5.10 it lists these reference types:
SCALAR
ARRAY
HASH
CODE
REF
GLOB
LVALUE
FORMAT
IO
VSTRING
Regexp
Only perl 5.10's ref seems to detect VSTRING refs and since they are deprecated and the usage seems to be rare, the module does not support them. Also FORMAT is only available in perl 5.8 and newer. But frankly, I can't imagine anyone creating refs/objects based on LVALUE, FORMAT or VSTRING (hmmm... maybe only TheDamian). So, they exist only for the sake of compatibility. For the Regexp type, I've just added a dynamic dependency on Data::Dump::Streamer if Scalar::Util::Reftype
is tried to be installed under anything older than perl 5.10.
The interface is simple. Just use
the module to get a brand new reftype
function:
use Scalar::Util::Reftype;
foo() if reftype( "string" )->hash; # foo() will never be called
bar() if reftype( \$var )->scalar; # bar() will be called
baz() if reftype( [] )->array; # baz() will be called
xyz() if reftype( sub {} )->array; # xyz() will never be called
$obj = bless {}, "Foo";
my $rt = reftype( $obj );
$rt->hash; # false
$rt->hash_object; # true
$rt->class; # "Foo"
reftype
will create an object based on the parameter you specified and it is possible to call test methods on the return value. It currently has these test methods:
scalar
array
hash
code
glob
lvalue
format
ref
io
regexp
scalar_object
array_object
hash_object
code_object
glob_object
lvalue_object
format_object
ref_object
io_object
regexp_object
class
Here, class
can be thought as analogous to Scalar::Util' s blessed function. It returns the package/class name of the reference if it happens to be a blessed reference. The rest of the methods test if the parameter matches the type they define.
Oh, I've also overloaded the object Scalar::Util::Reftype:reftype
returns, to be sure that it will not be used in boolean contexts. If one makes such a thing, then the code will suffer the consequences :)
I'm biased - I wrote UNIVERSAL::ref. I think you should be writing $thing->is_array instead of ref($thing)->is_array or Scalar::Util::reftype($thing)->is_array. That is, the thing itself should be responsible for telling you about itself and not some external bit of code.
I trend toward the Smalltalk end of the idea spectrum and I'd prefer that $thing be able to respond for itself. Outsourcing introspection prevents $thing from being able to intelligently decide to answer the question *differently* sometimes.
Also, when you're writing your ->is_array code, are you accounting for things that aren't arrays but act like them because of overloading?
package
...;
use overload '@{}' => 'act_like_an_array';