YA Perl Advent Calendar 2005-12-21

... is a list, no wait a tuple, noooo whatever you want to get me is fine. As its POD succinctly puts it, Want is a generalization of wantarray. This means, if you are so inclined, that instead of being limited to list/hash or scalar returns without the caller explicitly telling you what it wants in @_ you can now offer a smorgasborg of possibilities by relying upon that old perlish standby: context.

Want appears to have several advantages over wantarray beyond the variety of contexts you can detect; I say appears because I'm not wholly familiar with perl's guts or some of the features I'm about to relate. In particular:

You can niftily determine the number of return values required
Thus avoiding any unnecessary copying of large data structures, or allowing you a novel/contextual manner to specify a limit for depth of recursion to something like an implementation of the Fibonacci sequence.
Among other things perlsub has the following to say about lvalues:
         You can't use the return keyword, you must pass out the
         value before falling out of subroutine scope. (see
         comment in example above).  This is usually not a
         problem, but it disallows an explicit return out of a
         deeply nested loop, which is sometimes a nice way out.
Want actually gives you a return for use within lvalue subs, of course it's spelled differently...
Actually, speaking of lvalues the documentation says you'll get "Can't modify non-lvalue subroutine call in lvalue subroutine return." if you have a lvalue sub without an explicit return at the end. I actually see "Can't modify logical and (&&) in lvalue subroutine return at want.pl line 30, near '}'" under perl 5.8.0 and unless my weary eyes deceive me I may have seen another one altogether as well. So be careful of that. You're also supposed to use rreturn for rvalue contexts with lvalued subs but it seems return may work too under some circumstances.

A few final observations before I leave you with some sample code. First, note that the current implementation interface is not stable; it is only 0.09, even if it has taken 4 years to get there. Second, it's want('LIST') and not want('ARRAY'). And finally, order apparently matters: do your lvalue check first.


   1 use Want;
   2 use Lingua::EN::Numbers::Ordinate;
   4 my @days = <DATA>;
   6 sub days :lvalue {
   7   if( want('LVALUE', 'ASSIGN') ){
   8     my $n = shift;
   9     $days[$n] = want('ASSIGN');
  10     lnoreturn;
  11   }
  12   if( want('SCALAR') ){
  13     my $str = '';
  14     foreach( 1..12 ){
  15       $str .= sprintf $days[0], ordinate($_);
  16       $str .= join '', reverse(@days[1..$_]), "\n";
  17     }
  18     rreturn $str;
  19   }
  20   if( want('LIST') ){
  21     my $n = shift || want('COUNT');
  22     if( $n ){
  23       rreturn @days[1..$n];
  24     }
  25     else{
  26       rreturn @days;
  27     }
  28   }
  29   return;
  30 }
  32 #Rewrite the lyrics
  33 days(5) = "Five onion rings!\n";
  35 #Stringified
  36 print scalar days;
  38 #Equivalent subsets
  39 my @gifts = days(3);
  40 my($gift1, $gift2, $gift3) = gifts;
  41 (days)[1..3];
  43 __DATA__
  44 On the %s day of Christmas, my true love gave to me
  45 A partridge in a pear tree.
  46 Two turtle doves
  47 Three French hens
  48 Four calling birds
  49 Five golden rings.
  50 Six geese a-laying,
  51 Seven swans a-swimming
  52 Eight maids a-milking
  53 Nine ladies dancing
  54 Ten lords a-leaping
  55 Eleven pipers piping
  56 Twelve drummers drumming