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

I started using the Tie::Memozie routine yesterday and found that I could not get my custom EXISTS function to work. I think I may have found a couple of bugs, and I wanted to run it past the monks before opening a bug report. I have already notified the Module Author, but since Tie::Memoize is now included in the Core distribution I am planning to open a bug against perl5 core.

**update - the maintainer email address is bouncing, so no insights coming from that direction.

I passed a reference to this subroutine as my custom EXISTS handler with the assumption that it would always fail an exists check. My understanding was that this routine would only get called on exists test when there was a cache miss.

sub deep_exists { my ($key, $data) = @_; warn " Cache miss on exist key ($key) for ". $data->{container}{type} ." object\n"; return undef; }
Sure enough, it only runs when an exists test is done on an element that misses. To my surprise, when I tested existence of a non-existent hash element, I saw my warning and the exists check passed (as opposed to the expected failure).

So I decided to dig a bit deeper. Here is the code from the Tie::Memoize::EXISTS handler:

sub EXISTS { my ($a,$key) = (shift, shift); # STEP 1 return 1 if exists $a->[0]{$key}; # Have data # STEP 2 my $cache = $a->[1]{$key}; return $cache if defined $cache; # Existence cache # STEP 3 my @res = $a->[3]($key,$a->[4]); $_[0][1]{$key} = 0, return unless @res; # Cache non-existence # STEP 4 # Now we know it exists return ($_[0][1]{$key} = 1) if $a->[5]; # Only existence reported # STEP 5 # Now know the value $_[0][0]{$key} = $res[0]; # Store data return 1 }
from the Tie::Memoize perldoc :
The structure of the tied() data is an array reference with elements 0: cache of known values 1: cache of known existence of keys 2: FETCH function 3: EXISTS function 4: $data

There are two problems here that I see. They both crop up in STEP 3. First, the one that caused my specific problem is that the return value of the provided custom exist function is assigned to an array

my @res = $a->[3]($key,$a->[4]); $_[0][1]{$key} = 0, return unless @res;
So when I returned undef, it created an array with 1 element, which then satisfies the unless @res predicate.

According to the perltie documentation this is not the correct behavior for tied hash exists functions. I am able to work around this by returning the empty list instead.

The second potential issue is with the $_[0][1]{$key} = 0 assignment. It looks like the intent is to cache the fact that we already know this does not exist. That way subsequent exist tests will fail in STEP 2 without needed to call the test function again. However, since the data structure is shifted into the $a variable the correct way to accomplish this goal is $a->[1]{$key} = 0. That being said, I am assuming I understand the intent of this assignment. I may very well not understand what is being done here.

What do you think?

Replies are listed 'Best First'.
Re: Potential Tie::Memoize Bug
by chromatic (Archbishop) on Mar 16, 2005 at 19:35 UTC

    I can't explain the second issue, but the first is your bug. Returning undef explicitly to indicate falsehood is almost always an error. There are two circumstances where it's okay:

    • Where you've documented it explictly.
    • Where you use wantarray to do the right thing in list versus scalar context.

    I don't think the second case is that useful; bare return does the right thing for you.

      sub foo { return; } @ret = foo(); die "Not a Bug\n" unless @ret; print "Doh...shouldn't get here!\n"
      Results:
      Not a Bug
      Excellent! Thanks chromatic !

      I still find it curious that he calls the exists function in list context. I can't imagine I'm the only one who has been bitten by this.

      From the return perldoc for those too lazy to look it up themselves:

      return EXPR .......snip....... If no EXPR is given, returns an empty list in list context, the undefined value in scalar context, and (of course) nothing at all in a void context.
Re: Potential Tie::Memoize Bug
by nobull (Friar) on Mar 16, 2005 at 18:14 UTC
    I agree with your analysis. This is a bug.