Question

This is the expected and intuitive behavior of a tied hash to handle $h{a}++:

$ perl -E'
    sub DESTROY {}
    sub AUTOLOAD { say "$AUTOLOAD @_"; bless {} }
    tie %h, main;
    $h{a}++;
'
main::TIEHASH main
main::FETCH main=HASH(0x7fb6c982a188) a
main::STORE main=HASH(0x7fb6c982a188) a 140423041558889

This is also expected to handle a nested case $h{a}{b}++, however I can't figure out why, nor, most importantly, how to override this behavior:

$ perl -E'
    sub DESTROY {}
    sub AUTOLOAD { say "$AUTOLOAD @_"; bless {} }
    tie %h, main;
    $h{a}{b}++;
'
main::TIEHASH main
main::FETCH main=HASH(0x7fbff102a188) a

I've stumbled upon this while testing the Tie::CHI. It is clearly capable of storing complex objects:

$ perl -MDateTime -MTie::CHI -E'
    tie %c, "Tie::CHI", { driver => "File", root_dir => "." };
    $c{a} = DateTime->now;
'

And Perl is capable of retrieving any data from the deeply nested structures, also:

print Dumper $c{json}->{results}[0]{geometry}{location};

But storing inside nested structures won't work in any tie()'d hash:

$c{json}->{results}[0]{geometry}{location} = [0, 0];

Could Tie::CHI be patched to handle this case with more DWIMmery? If not, what's the alternative?

Was it helpful?

Solution

Do you realize that

$h{a}{b}

is exactly the same thing as

$h{a}->{b}

and that is consists of two separate hash lookups? It's basically the same thing as

my $anon = $h{a};
$anon->{b}

You want %{ $h{a} } to be a tied hash, but $h{a} (FETCH) returned a reference to an untied hash. Change it to return a tied hash.

$ perl -E'
    sub DESTROY {}
    sub FETCH { say "FETCH @_"; tie my %h, main; \%h; }
    sub AUTOLOAD { say "$AUTOLOAD @_"; bless {} }
    tie %h, main;
    $h{a}{b}++;
'
main::TIEHASH main
FETCH main=HASH(0x8006c030) a
main::TIEHASH main
FETCH main=HASH(0x80071e28) b
main::TIEHASH main
main::STORE main=HASH(0x80071e28) b 2147952185

OTHER TIPS

As it follows from ikegami's answer, to properly implement nested HashRefs in Tie::CHI it would be necessary to make a recursive converter which traverses the data structure wrapping the hash/array references with tie() interface. Not cool at all.

Fortunately, there's more than one way to do it, and DBM::Deep reveals itself as a rock-solid alternative to the whole CHI namespace.

#!/usr/bin/env perl
use strict;
use warnings;

use Data::Dumper;
use DBM::Deep;

tie my %c, 'DBM::Deep', 'test.db';

$c{json}->{results}[0]{geometry}{location} = [0, 0];
print Dumper \%c;

This produces exactly what I need by encapsulating every reference as a DBM::Deep::(Array|Hash):

$VAR1 = {
    'json' => bless( {
            'results' => bless( [
                    bless( {
                            'geometry' => bless( {
                                    'location' => bless( [
                                            '0',
                                            '0'
                                        ], 'DBM::Deep::Array' )
                                }, 'DBM::Deep::Hash' )
                        }, 'DBM::Deep::Hash' )
                ], 'DBM::Deep::Array' )
        }, 'DBM::Deep::Hash' )
};

And, besides the ::File engine, DBM::Deep also support the ::DBI engine.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top