YA Perl Advent Calendar 2005-12-15

Complicated perl data structures are built up with references. That's fine and dandy, easy to pass around etc. etc. But in the passing, what if you need to make changes? Generally modifying somebody else's data structure is not considered a nice thing to do. Well then, you could copy it. But being reference based you'd end up with a shallow copy i.e; all the internal references would be the same and you'd still by changing somebody else's data. Well then, you could try to minimize the scope of what you touch with my or local but that can be really messy and error prone, especially if you don't know what you'll be touching or others didn't plan on you putting your grubby paws all over everything. So, what to do? Enter Data::COW. MoooooOOO

COW stands for Copy On Write, and is implemented exactly how you would expect. In fact, the implementation is a lot like object inheritance. You give the COW a reference and it returns the favor. You then proceed to use this new reference in the same manner as you would have the original but are free to maul it at will, or not... PETA might get mad. To begin with, fetching data from the referenced data structure simply gets the value from the parent because the child doesn't "implement the method". When you store, or change, a value in the structure that change is only made to your child structure and these modifications will be used when fetching values in the future instead of accessing the parent. Perhaps an example will help...

$struct = {
            'b1f' => 'I like meat!',
            'fib' => [
                       1,
                       1,
                       2,
                       3
                     ]
          };
Make changes here...
$calf = {
          'fib' => [
                     '1',
                     '1',
                     '2',
                     '3',
                     '5',
                     '8',
                     '13'
                   ],
          'b1f' => 'I like meat!'
        };
$struct = {
            'b1f' => 'I like meat!',
            'fib' => [
                       1,
                       1,
                       2,
                       3
                     ]
          };
$calf = {
          'fib' => [
                     '1',
                     '1',
                     '2',
                     '3',
                     '5',
                     '8',
                     '13'
                   ],
          'b1f' => 'I like meat!'
        };
Eat more chickin
And now, on with the code!

mod15.pl


   1 use Data::COW;
   2 use Data::Dumper; #No jokes about buffalo biscuits please
   3 
   4 my $struct = {
   5 	      fib=>[1,1,2,3],	      
   6 	      b1f=>'I like meat!'
   7 	     };
   8 
   9 print Data::Dumper->Dump([$struct], ['struct']);
  10 
  11 print "Make changes here...\n";
  12 my $calf = make_cow_ref $struct;
  13 push @{$calf->{fib}}, 5, 8, 13;
  14 
  15 print Data::Dumper->Dump([$calf], ['calf']);
  16 
  17 
  18 print Data::Dumper->Dump([$struct, $calf], [qw'struct calf']);
  19 
  20 #Okay fine, but a deep copy would look the same...
  21 #...but references would be different, even for unmodified values
  22 print "Eat more chickin\n" if $struct->{b1f} eq $calf->{b1f};

See Also

Clone