Identity.pm: A Very Funny Module

Beginners always want to write this:

  print "The sum of three and four is: 3+4";

And they want the 3+4 part to be evaluated, so that it prints this:

      The sum of three and four is: 7

Of course, it's a double-quoted string, so it's not evaluated. The only things that are evaluated in double-quoted strings are variable references. Maybe the thing that confuses beginners is that there are so darn many ways to write variable references in Perl:

 print "$a and @a and $a[3] and $a{Ouch} and ${$a{Radikal}[1]}->[2]";

You sure can get a lot of stuff in there! And that leads people to use the following rather ugly and obfuscated construction when they want to interpolate the results of a function call into a string:

  print "The return value is: @{[&function(arg)]}";

Wow, like, totally E2MUCHPUNCTUATION, dudes.

The @ tells Perl to interpolate an array; the {...} tells Perl to expect a reference instead of an array name; the [...] symbols construct an anonymous array and yield a reference to it, and inside a @{...} construction in a double-quoted string, Perl does evaluate expressions.

There are three problems with this trick. It's ugly. It's obfuscated. And under some circumstances, it's wrong. The first two problems are obvious. Real obvious. The third is subtler: The function is evaluated in list context, and sometimes that's what you want, and sometimes it isn't. An example of when it isn't: You'd like to print the date, which is conveniently returned by the localtime function when it's invoked in scalar context:

  $a = localtime;
  print "It is now $a.\n";
      It is now Sun Mar  8 01:19:19 1998.

But if you try using the ugly construction:

  print "It is now @{[localtime]}.\n";
      It is now 19 19 1 8 2 98 0 66 0.

See, we really did need scalar context and not array context there.

There are solutions to this, but they make the already ugly and obfuscated construction even uglier and more obfuscated. Here are some examples:

  print "It is now ${\do{my $a = localtime}}.\n";
  print "It is now ${my $a = localtime; \$a}.\n";
  print "It is now @{[scalar localtime]}.\n";

But I have a solution. It's clean. It's simple. It's attractive. Unfortunately, it's also somewhat ridiculous.

  1. Check out sample demonstration program #1 and sample demonstration program #2.
  2. Then, when the beginnings of a horrified suspicion have begun to grow in your mind, look at the source code.
  3. You can download all three files in one package if you want to try it out at home, which I recommend.

If you're amused or horrified (and I think you should be,) drop me a note and let me know. I'm really pleased with this. I'm also trying to decide what to call it before I upload it to CPAN, so if you have any suggestions, please suggest them. Yes, you heard right: I'm really planning to inflict this on the rest of the world.


Thoughts

Don't read this until you've looked at the sample programs, or might might not make sense, and it might contain spoilers.

  1. I'm thinking of ditching $num entirely; it's not clear to me that it is ever useful for anything that $str isn't also useful for. Your feedback here would be helpful.
  2. Matthias Neeracher suggests naming the module Interpolation. I think this is a pretty good idea. Are there any other good ideas?
  3. Interpolation is a better name, because you don't have to use the identity function in the tie. For example, suppose you're going to be interpolating a lot of reversed strings for some stupid reason, and you wish there was a \R...\E modifier to reverse a string, the way that \U...\E upcases one. You could insert reverse into the FETCH and then use "This string is $R{in reverse}." as a way to get "This string is esrever ni.". This shows that this technique is actually surprisingly powerful. Now that I've thought of this, I've moved a lot more towards the `useful' side of the argument, and away from the `idiotic' side. I'll put in tie options to support this after lunch.
  4. Here's a more plausible example of the last point. You have a variable, $STATE, which contains the name of a U.S. state or Candian province, but it came from a database, so the capitalization is uncertain, and you don't want to alter it for some reason, maybe because you'll need it as a key to look up more stuff in the database. You could copy $STATE to a new variable and then operate on that variable, but it seems like a lot of code just to get something to print out right, and you'll have to do a similar task in many places to format many strings; that srtategy also ends up obscuring the important parts of your program.

    But you don't want to go printing out

          I see you live in ALBERTA
    
    or whatever capitalization ugliness it might contain. You also can't use
      "I see you live in \u\L$STATE\E"
    
    because that gives the wrong result for New Brunswick: You get
          I see you live in New brunswick
    
    and the b is lowercase when it should be uppercase.

    The solution is to use this module; now it's easy to write

      "I see you live in $ucwords{$STATE}"
        I see you live in New Brunswick
    
  5. Doug Beaver suggests (among other things) a much more convincing example of a useful interpolator: commify (See perlfaq5). For example:
      $SALARY = 35000;
      print "Your salary is $money{$SALARY}";
    
    could print
          Your salary is $35,000.00
    
  6. Interpolation.pm was released on 14 March, 1998. It looks really good. It should be on your favorite CPAN site by 16 March, or sooner. Check it out.

Return to: Universe of Discourse main page | What's new page | Perl Paraphernalia

mjd-perl-identity@plover.com