Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Re: Reference assessment techniques and how they fail

by dragonchild (Archbishop)
on Feb 17, 2008 at 04:35 UTC ( #668386=note: print w/ replies, xml ) Need Help??


in reply to Reference assessment techniques and how they fail

So, what is your recommendation for the following:

# I want to know if it will behave as a hash. sub isHash { # What goes here? } sub isScalar {} sub isArray {} sub isFunc {} sub isObject {}
Remember to also take tying into account. :-)

My criteria for good software:
  1. Does it work?
  2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?


Comment on Re: Reference assessment techniques and how they fail
Download Code
Re^2: Reference assessment techniques and how they fail
by kyle (Abbot) on Feb 17, 2008 at 12:26 UTC

    I don't see how a tied hash (ref) is a special case.

    use strict; use warnings; package Nothing; sub isa { 0 } package NotHash; use overload '%{}' => sub { {} }; sub new { bless [], shift } package OverHash; use overload '%{}' => sub { {} }; sub new { bless {}, shift } package main; use Tie::Memoize; use Test::More; my @test_cases = ( { name => 'unblessed hash', test => {}, is_hash => 1, }, { name => 'unblessed array', test => [], is_hash => 0, }, { name => q{hash blessed as 'HASH'}, test => bless( {}, 'HASH' ), is_hash => 1, }, { name => q{array blessed as 'HASH'}, test => bless( [], 'HASH' ), is_hash => 0, }, { name => q{hash blessed as 'ARRAY'}, test => bless( {}, 'ARRAY' ), is_hash => 1, }, { name => 'hash in package 0', test => bless( {}, '0' ), is_hash => 1, }, { name => 'array in package 0', test => bless( [], '0' ), is_hash => 0, }, { name => 'hash with ->isa overridden', test => bless( {}, 'Nothing' ), is_hash => 1, }, { name => 'blessed array with %{} overloaded', test => NotHash->new(), is_hash => 1, }, { name => 'blessed hash with %{} overloaded', test => OverHash->new(), is_hash => 1, }, { name => 'tied hash', test => get_tied_hash(), is_hash => 1, }, { name => 'not a reference', test => 'HASH', is_hash => 0, }, ); sub get_tied_hash { tie my %h, 'Tie::Memoize', sub {}; return \%h; } plan 'tests' => scalar @test_cases; foreach my $test ( @test_cases ) { is( !!isHash( $test->{test} ), !!$test->{is_hash}, $test->{name}); } use Scalar::Util qw( reftype blessed ); sub isHash { my $suspected_hash = shift; return 0 if '' eq ref $suspected_hash; return 1 if 'HASH' eq reftype $suspected_hash; if ( blessed $suspected_hash && overload::Method( $suspected_hash, '%{}' ) ) { return 1; } return 0; }

    The code for isHash is based heavily on blokhead's from "Is it a hashref" vs "Can I use it like a hashref?"

    Have I missed an important test case here?

      %{} is set, but shouldn't respond because nothing has been set.
      my $scalar = 'abcd'; my $obj = Object::MultiType->new( scalar => $scalar );
      The whole tying thing is, for example, DBM::Deep. dbm-deep provides both TIEARRAY and TIEHASH and all appropriate methods in subclasses. Those methods didn't have to be in subclasses. It might be useful to test.

      Also, reftype(), when implemented in pureperl (such as when installed without a compiler), reblesses to a string that shouldn't be a class, but might. That should be tested.

      Frankly, I would be more interested in figuring out how to make sub isHash { eval { %{ $_[0] }; 1 }; } work without warnings. That seems to be a saner solution because it has perl figuring out how Perl works.


      My criteria for good software:
      1. Does it work?
      2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

        I'll start by saying I don't really understand your remarks about DBM::Deep and tie.

        use Object::MultiType; my $scalar = 'abcd'; my $obj = Object::MultiType->new( scalar => $scalar ); print %{$obj} ? 'hash' : 'empty hash', "\n"; __END__ empty hash

        Object::MultiType does not die here, so eval would see it the same as everything else. (I had a much longer test set for this, but it doesn't really add anything to what I put in Re^4: Reference assessment techniques and how they fail. Specifically, eval has the side effect of calling overload methods, but blokhead's test doesn't.)

        We seem to be talking about a difference between the interface as presented and the interface as implemented. That is, maybe it says it can be a hash, but then it croaks when you try to treat it as one. I think there's a whole range of non-function between total failure and total invulnerability. One side of that range (total failure) is where eval outperforms less invasive checks. The other advantage it seems to have is future-proofing against someday there being another kind of scalar that that acts as a hash but doesn't match the less invasive checks. Are those two advantages bigger than determining type without possibly calling unknown code?

        (In case it's not obvious, the subtext of my original article is that determining type—reliably—should be easier than this.)

        Looking at the code for Scalar::Util::reftype, I don't see where it reblesses. Its vulnerability seems to be that it relies on the object not to have a particular instance method (a_sub_not_likely_to_be_here), which it has defined in UNIVERSAL. Elsewhere refaddr blesses into 'Scalar::Util::Fake', apparently to keep from calling an overloaded stringifier.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://668386]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (9)
As of 2014-12-25 12:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (160 votes), past polls