gmpassos has asked for the wisdom of the Perl Monks concerning the following question:

How to test a tied HASH as a boolean?

Take a look in this code, and you can see that the HASH is always FALSE as boolean.

my %hash ; tie(%hash , 'TiedHash') ; print "key: $hash{key}\n" ; if ( %hash ) { print "BOOL: 1\n" ;} else { print "BOOL: 0\n" ;} package TiedHash ; use overload ( 'bool' => sub{ print "OVER1!\n" ; } , ## print() return 1! '""' => sub{ print "OVER2!\n" ; } , '0+' => sub{ print "OVER3!\n" ; } , 'fallback' => 1 , ); sub TIEHASH { bless({}, __PACKAGE__ ) ;} sub FETCH { return( 'fetch_val' ) ;}
This was test on Perl-5.6.1-Win32 and Perl-5.8.0-Win32, with and without use overload and it always return FALSE!

Looking for a way to return TRUE for a tied HASH, I found that it looks for the value of the HASH not tied. Soo, the code that work is:

my %hash = (1) ; tie(%hash , 'TiedHash') ; if ( %hash ) { print "BOOL: 1\n" ;} else { print "BOOL: 0\n" ;} package TiedHash ; sub TIEHASH { bless({}, __PACKAGE__ ) ;}
In other words, the HASH need to have at least one KEY before TIE it, or the HASH will be FALSE as boolean!

I don't know if this is documented, but haven't found anything about it. I also think that it looks like a bug, since the value of the HASH as boolean (and string) need to be controled and changed (now is like a constant after tie it). The right way is to control this through overload or from some sub inside the TiedHash package (like FETCH).

Well, for me I just need to always have the HASH as TRUE, since I tie it only when I have keys. But in the future this can be changed in Perl, and my code wont work! I holp that I have helped some monks that had this problem too. ;-P

Graciliano M. P.
"The creativity is the expression of the liberty".

Replies are listed 'Best First'.
Re: Tied HASH as boolean?!
by hossman (Prior) on Jun 08, 2003 at 03:21 UTC

    I'm no authority on tied objects, or on overload ... but i think you are confussing the two.

    By overloading 'bool' in your package, you can define the behavior of evaluating an object of that package in boolean context -- but I don't think that's what is happening in your code.

    By tieing your obejct as a hash, only the TIE* methods for hashes are ever called on your object. When you say: "if (%hash)" it doesn't matter that you have overloaded 'bool' for your package, all that matters is that you are evaluating a hash in a bolean context, which is 'true' if-and-only-if that hash has any elements.

    Consider these modifications to your script (and teh output)...

    #!/usr/bin/perl -wl use strict; my %hash ; my $obj = new TiedHash(); tie(%hash , 'TiedHash') ; print "checking Hash...."; print 'BOOLHASH' if ( %hash ); print "checking Object...."; print 'BOOLOBJ' if ( $obj ); package TiedHash ; use overload 'bool' => sub{ print "BOOL"; return 1 ; } , '""' => sub{ print "STRING"; return 'tiedhash' ; } , ; sub TIEHASH { bless({}, __PACKAGE__ ) ;} sub new { return TIEHASH; } __END__ checking Hash.... checking Object.... BOOL BOOLOBJ

    ...I'm not sure what methods on your tied hash are getting called when %hash is evaluated in boolean context, but i'm sure if you override all of the TIE* methods, you can figure it out.

Re: Tied HASH as boolean?!
by sauoq (Abbot) on Jun 08, 2003 at 03:16 UTC

    I don't have an immediate answer for you, but I can tell you that overload isn't working for you because it doesn't overload the behavior of the hash you tie, it overloads the behavior of the object that the hash is tied to. I.e. the return value of tie().

    -sauoq
    "My two cents aren't worth a dime.";
    
      Yes I know that the tie object and the behavior of the HASH are diferent. But what I tell, is that we need to control the value of the HASH as boolean, as we control the values of keys, and one way that should be is through overload. I'm just pointing how it should work, since now I think that can't control that is a bug in tie.

      Graciliano M. P.
      "The creativity is the expression of the liberty".

        I agree that there should be a way to do it but I don't think using overload would be the right way. Either one of the TIE* methods should be used or one should be added to give a hook allowing the programmer to set the value of a hash in scalar context.

        Purely as a possible workaround, if you implement FIRSTKEY, NEXTKEY, and STORE, you can get a more useful truth value by forcing list context on the hash.

        use Tie::Hash; tie my %h, 'Tie::StdHash'; # Check with an empty %h first... print "TRUE 1\n" if ( %h ); # Nope not true. print "TRUE 2\n" if ( ()=%h ); # Not true either. # Now a non-empty %h ... $h{foo} = "bar"; print "TRUE 3\n" if ( %h ); # Nope not true. print "TRUE 4\n" if ( ()=%h ); # Yes!
        Of course, this comes with all sorts of huge caveats. Depending on the size of the hash it might not be realistic at all to do this for performance reasons. Your specific tie implementation might even make it an impossibility. (For instance, your list of keys needs to be finite; if it isn't the program will hang until it dies trying to allocate memory.) So, it's not an ideal solution, but just the same, it might work in many cases.

        It really is too bad that you can't use syntax as simple as if ( %h ) { ... }.

        -sauoq
        "My two cents aren't worth a dime.";
        
Re: Tied HASH as boolean?!
by adrianh (Chancellor) on Jun 08, 2003 at 19:31 UTC

    I'm afraid you're out of luck.

    The value of a tied hash in a scalar context is currently undefined. When 5.8.1 eventually rolls around you'll get a fatal error if you try. See this perl5-porters summary for more details.

    Yet more discussion can be found in the perl5-porters mailing archive here, here and here.

    As sauoq correctly pointed out overload shouldn't work since it applies to the underlying object used to implement the tied hash.

    The addition of an extra hash method to the tie API this was discussed, and might appear later (at least that's my interpretation of the bug's history.)

Re: Tied HASH as boolean?!
by mojotoad (Monsignor) on Jun 09, 2003 at 19:56 UTC
    Well I cannot resist a reference to my very first post here on perlmonks:

    Whither the truth of a tied hash? (Apr 28, 2000)

    The short answer is to use scalar keys %Hash, but I still consider this behavior to be an irritant.

    Matt

      Thanks!

      But this is just to enable the end user (a Perl programmer) to use that hash as a normal hash in the framework! But it actually don't know when it's tied or not, since I only tie it to can see the keys of 2 different HASHes at the same time.

      Just to know the story. This is for HPL (that I will release for the public soon). When you have POST or GET inputs, they are parsed and stored in the HASHes %POST and %GET (since you can send the both at the same time). But from previous versions this were in the HASH %FORM. Now the HASH %FORM still exists, and is just an alias in the table entry to %POST or %GET, depending of the situation. But when you have the both, %FORM need to have the content of %GET and %POST at the same time, so, I tie it and make it work/look in the 2 HASHes, insted of just copy the content (what is wrong).

      But the user really need to can test if %FORM is true, since this code is very usual to know if you have formulary inouts:

      if ( %FORM ) { ...response... } else { ...show the formulary... }

      Graciliano M. P.
      "The creativity is the expression of the liberty".