Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Burak (3156)

Burak
  (email not shown publicly)
http://www.burakgursoy.com/

Journal of Burak (3156)

Wednesday September 09, 2009
10:24 PM

reftype() lazziness

[ #39605 ]

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 :)

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • 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*