Concerning code factorization, efficiency and tied variables:

In the wake of Are lvalue methods (as properties) a good idea?, I've revisited an old node of mine, To Validate Data In Lvalue Subs. One thing that I realize now is that I didn't make clear how independent the Constrained class is from the notions of closures and lvalue subs. Constrained can be applied to any scalar variable, including references to anything.

What good is that? A tied interface is slower than simple assignment for a reason; it's doing something. The 40% (or whatever) extra overhead for assigning or fetching through the tied interface is trivial if you have work to do inside it. What work? Whatever is ubiquitous enough to factor this way! I think that that is more than "syntactic sugar", and it's sweeter, too.

For reference, here is a slightly updated and renamed version of Constrained, package Tie::Constrained;

use Errno qw/EINVAL EDOM ERANGE/; sub TIESCALAR { my $class = shift; my $self = { test => defined $_[0]? $_[0]: \&validate }; $self->{test}($_[1]) or invalid(EINVAL) if defined $_[1]; $self->{val} = $_[1]; bless $self, $class; } sub STORE { my ($self, $try) = @_; $self->{test}($try) or invalid(EINVAL); $self->{val} = $try; } sub FETCH { $_[0]->{val}; } sub DESTROY {} sub validate { 1 } sub invalid { $! = shift; die sprintf("Constraint violation: %s by %s::%s in %s line %s.\n", $!, map { qq($_) } (caller 1)[0,3,1,2] ); } 1; __DATA__
Usage: use Tie::Constrained; tie my $var, Tie::Constrained => \&mytest, $initval; Both arguments are optional, but the default validator function always says yes. mytest() should be designed to return true for valid data and false for data to reject.
That's much like the older code, but I've removed the stringency from FETCH() and added a dummy default validator. Those changes were to make subclassing easier. A subclass would typically override invalid() to get different error handling or validate() to have a common class-wide default test.

Data validation is the job the Tie::Constrained class does. That's an example of something you may want to do many times, the same way each time, every time a mutator is applied to your variable.

Off the top of my head, I can think of three basic ways to code those tests.

  1. Paste in a call to a validator function after each mutation. At least, just before each use where the validity matters.
  2. Bless the variable into a class which overloads mutators to validate.
  3. Tie to Tie::Constrained

The first and most obvious one is dismal in practice. The mutation is done to your variable before you get to validate. You can't apply it to third-party code. Paste errors may gum you up. Maintainance is a nightmare. The limited checking of 1b) does nothing to inform you where the bad data crept in. The code has flashing neon signs saying "Factor me!"

The second is better, but it has problems of its own. The proliferation of classes confuses development. Overriding core functions and overloading core operators confuses everybody. The class packages represent a lot of perhaps tricky code to write. Factorization is pretty good, but nothing like . . .

Three. Once a variable is tied to its very own automatic validity check, every mutator will be checked before the variable is modified. That is true of old code and new, third-party code, perl modules, all without the code needing to know anything about it. No infrastructure at all. No special coding beyond the tie call.

That is code factoring with a vengence. I also consider it a particularly sparse and clean kind of OO code, where the object is the aggregate of variable, test and exception.

I'm considering doing a little more tuning and much pod writing to prepare a distribution for CPAN. I'll welcome your comments.

After Compline,
Zaxo


In reply to Tie Me Up, Tie Me Down by Zaxo

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.