This all started with thread sorting a vec. I thought the thread was interesting, and also was BrowserUK's reply, so I went to CPAN and did some search. A module called Tie::VecArray came to my attention.

I played with that module a littel bit, and quickly decided that it was a bad example of what tie was about, and how it should be implemented. I think this is a good lesson that worth to share with monks.

I am not here to define tie, as it is defined already, and for anyone interested, you can check out perltie documentation. But I am interested in giving my view of tie from an OO perspective. My understanding is that tie is a type of interface, which defines a common set of methods that allow you to access an object, regardless of its internal structure.

For example, Tie::Array allows you to access the object tied to an array, as if it is an array without knowing its internals. You can get array element thru [], do things like shift, unshift, push, pop etc. (if they are implemented, but they are not always mandatory)

Now back to the main topic, why is that module bad? Let's look at this example (I have shamlessly stolen the print unpack syntax from BrowserUK:

use strict; use warnings; require Tie::VecArray; my $vec = ''; my @array; vec($vec, $_, 4 ) = rand(16) for 0 .. 99; print join(" ", map{ vec( $vec, $_, 4) } 0 .. 99); print "\n"; my $obj = tie @array, 'Tie::VecArray', 4, $vec; @array = sort {$a <=> $b} @array; print join(" ", map{ vec( $vec, $_, 4) } 0 .. 99);

To me (and I believe to most people), the expected result is that, sort the array will actually sort the vec. However it turened out to be wrong, that sort does not affect the vec at all. So I started to look into the implementation of Tie::VecArray, and I discovered that inside the package, it made a copy of the vec at the construction time, and all the operations later against the array only affect the temperary internal copy of the vec, not the vec itself any more.

This might make sense, if the Tie is read only. However all the examples in the documentation of Tie::VecArray, and also the fact it has STORE, STOESIZE etc. implemented, clearly indicated that it was done as a writable tie.

Who care to write to the temperary internal copy of the vec?

Interesting enough, that module actually passed CPAN testing on most platforms, other than Solaris. I am really curious as what test cases they used, and who decided that those cases were sufficient. Other than the technical lesson, it does ring a bell to me, that one has to be very careful about the quality of CPAN modules, especially those ones that are not well-known, and you have to be prepared to make your own judgement.

The immediate problem could be easily fixed by keeping a ref to the vec instead of copying the content of the vec (The code is only targeted to fix this particualr problem):

package Tie::PGVecArray; use strict; use warnings; use Tie::Array; use POSIX qw(ceil); use base qw(Tie::Array); use fields qw(bits vec size); sub _IDX2BYTES { my $self = shift; my $idx = shift; #define _IDX2BYTES($self, $idx) \ ceil($idx * ($self->{bits}/8)) } sub _BYTES2IDX { my $self = shift; my $bytes = shift; #define _BYES2IDX($self, $bytes) \ ceil($bytes * 8 / $self->{bits}) } sub TIEARRAY { my($class, $bits, $vec) = @_; no strict 'refs'; my $self = bless [\%{$class.'::FIELDS'}], $class; $self->{bits} = $bits; $self->{vec} = $vec; $self->{size} = _BYTES2IDX($self, length $$vec); return $self; } sub FETCH { my $self = shift; return vec(${$self->{vec}}, $_[0], $self->{bits}); } sub STORE { my $self = shift; $self->{size} = $_[0] + 1 if $self->{size} < $_[0] + 1; return vec(${$self->{vec}}, $_[0], $self->{bits}) = $_[1]; } sub FETCHSIZE { my $self = shift; return $self->{size}; } sub STORESIZE { my $self = shift; my $new_size = shift; if( $self->{size} > $new_size ) { my $new_length = _IDX2BYTES($self, $new_size); substr(${$self->{vec}}, $new_length) = '' if $new_length < length ${$self->{vec}}; } $self->{size} = $new_size; } 1;

Now if you try this modified package with the following code (almost the same as the first piece of code, and the only difference is that this time I use Tie::PGVecArray, instead of Tie::VecArray), you will see that the vec is sorted, when you sort the array it tied to:

use strict; use warnings; require Tie::PGVecArray; my $vec = ''; my @array; vec($vec, $_, 4 ) = rand(16) for 0 .. 99; print join(" ", map{ vec( $vec, $_, 4) } 0 .. 99); print "\n"; my $obj = tie @array, 'Tie::PGVecArray', 4, \$vec; @array = sort {$a <=> $b} @array; print join(" ", map{ vec( $vec, $_, 4) } 0 .. 99);

In reply to A good lesson on Tie (through a negative example) by pg

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.