YA Perl Advent Calendar 2005-12-06

Today I bring you a little chunk code of originally created by everybody's favorite octopus-keeping infinite number of monkeys—MJD. The module, known simply as Interpolation, has been embraced and extended by the current maintiner—Jenda.

Loosely speaking you could think of Interpolation as providing a smattering of predefined and customizable overloaded quoting. It's not of course, it instead relies upon abuse of tie. The premise is that you index into a hash and the key is formatted and interpolated into your string. As the pod says

   i use Interpolation name => \&function, ...;
  ii print "la la la la $name{blah blah blah}";
 iii 
  iv # This is like doing:
   v $VAR = &function(blah blah blah);
  vi print "la la la la $VAR";

A deceptively simple and powerful piece of syntactic NutraSweet.

Below is a code listing with some examples of my testing various module features. All in all it's pretty nifty and I might begin including it in my repertoire however I strongly urge you to heed the Caveats, especially Trap 1 because you'll surely run into this if you try anything fancy. I experienced a similar problem with the ucwords example and had resort to non-interpolating quotes for the key. Finally the sqlescape function seems rather goofy, the suggested binding to %' is cute–giving the appearance of being enclosed in 'uotes !! Not to be confused with the perfomance penalty inducing $'– but the Microsoft-esque escaping is ugly.

ADDENDUM: eval is a bit of a misnomer for identity/null. None of the names are quite right since it returns the result of the EXPR provided as the key, so it's not identity. This is not the same thing as eval because it limits you to a single statement. And

     $_ = $eval{'use int; 22/7'};
is pass through unaltered; the outer quotes are needed to prevent perl from misparsing the embedded semi-colon. I imagine null is supposed to be read "noop", which isn't exactly true either. In any event an actual eval would be useful and you can implement it yourself with the following:

     use Interpolation eval=>sub{ eval shift };

mod6.pl


   1 use Interpolation ucwords=>ucwords, "'"=>sqlescape, round=>round, eval=>'eval',
   2   title=>\&titleCase;
   3 use constant PI => 4 * atan2(1, 1);
   4 
   5 #Let's checkout round and eval
   6 {
   7   #Note the syntax when calling round to force PI to be seen as an expression
   8   print "Urban Legend: AL tried to define Pi as $round{+PI}\n",
   9     "To this day a unit circle in Hunstsville has an area of $eval{1**2*PI}\n";
  10   
  11   #=Urban Legend: AL tried to define Pi as 3
  12   #=To this day a unit circle in Hunstsville has an area of 3.14159265358979
  13 }
  14 
  15 #ucwords
  16 {
  17   #You can even use it without Interpolation
  18   #Is this starting to sound a little like a former featured module?
  19   print $ucwords{'an easy interface akin to the hard to remember sequences: \u\L and \l\U'}, "\n";
  20 
  21   #=An Easy Interface Akin To The Hard To Remember Sequences: \U\L And \L\U
  22   #...and it works on every word too
  23 }
  24 
  25 #sqlescape
  26 {
  27   #Can't be bothered with placeholders?
  28   my $lastname = q(O'Reilly); sub Select;
  29 
  30   Select "firstname From ACCOUNTS Where lastname=$'{$lastname}'";
  31   #=Selecting... q<firstname From ACCOUNTS Where lastname='O''Reilly'>
  32 }
  33 
  34 #custom titleCase
  35 {
  36   sub titleCase{
  37     my @F = split /\b/, $_[0];
  38 
  39     #This is the kind of nonsense this module is intended to avoid
  40     join '', "\u\L@{[shift @F]}",
  41       map { /^(?:a|the|in|at|on|near|over|an)$/i ? lc $_ : "\u\L$_" } @F;
  42   }
  43   print $title{'HORTON hears A who'}, "\n";
  44   #=Horton Hears a Who
  45   print $title{'The bridge on the River Kwai'}, "\n";
  46   #=The Bridge on the River Kwai
  47 }
  48 
  49 
  50 #Pay no attention to the man behind the curtain
  51 sub Select{
  52   print "Selecting... q<$_[0]>\n";
  53 }