I passed some of these objects into H::T but it didn't work, failing with Single reference arg to param() must be a hash-ref! You gave me a Foo. Dang! I looked in the code and saw that it uses UNIVERSAL::isa to check when it expects a data structure to be a hashref:
Well, this is a bummer, I thought. UNIVERSAL::isa (among its other shortcomings for determining the type of a reference) doesn't take overloading into account. I finally had to declare an empty package HASH and put it in @Foo::ISA to get the objects to work cleanly with H::T. Yuck. Not a very elegant solution at all.my $type = ref $first; ... croak("...") unless $type eq 'HASH' or (ref($first) and UNIVERSAL::isa($first, 'H +ASH'))
It dawned on me that asking "is this thing really a hashref?" seems like poor coding -- we are demanding to know something about the implementation of an object. Instead, we should only need to ask things about the object's interface, like "can I use this thing like a hashref?" After thinking about it, I would guess that 90% of the time someone asks that first question, their intent would be better captured by asking the second question. Hopefully the example above with HTML::Template illustrates this.
An easy way to check for hash-deref-ability would be to try eval { %{$thing} } and check for failure in $@. But if $thing is overloaded for %{}, this operation could potentially have side-effects for $thing. It would be better to have a way that doesn't introduce possible Heisenberg problems.
This is what I came up with, using utility functions from Scalar::Util and overload:
It handles overloaded cases correctly, and is not fooled by the same silly cases (ie, bless [] => "HASH") that ref and UNIVERSAL::isa are.use overload (); # added (), according to ysth's suggestion use Scalar::Util qw/blessed reftype/; use Carp; ## call this function as: can_deref_as($ref, $type) sub can_deref_as { my ($thing, $type) = @_; my %overload = ( HASH => '%{}', ARRAY => '@{}', SCALAR => '${}', GLOB => '*{}', CODE => '&{}' ); croak "Invalid reference type $type" unless $overload{$type}; return 0 unless ref $thing; return 1 if $type eq reftype $thing; return 1 if blessed $thing and overload::Method($thing, $overload{$type}); return 0; }
I wonder if this function should be included in Scalar::Util (or put in a separate module somewhere), I may email the maintainer to get his opinion. In any case, it would sure be nice for modules like HTML::Template to check for hash-deref-ability instead of checking the true reference type, so we can all use objects with hash-dereferencing overloaded.
What are your thoughts?
blokhead
In reply to "Is it a hashref" vs "Can I use it like a hashref?" by blokhead
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |