using the sphincter-sigil to abuse Perl

(Sorry about the title. Don’t you hate it when you’ve got a syntax question and you don’t even know what to google to find out what it means…?)

draegtun dropped by recently to offer suggestions for tuning up some of my Perl code… and some of his code appeared to do some strange things. (Thanks for the help, as always….) Maybe it was something to do with closures and maybe it was operating directly on the internal function table of Perl. So I decided to mess with it some before rolling it out to my mini-project. I’m sure draegtun ‘s a nice guy and all, but I remember all to well the poor wannabe lisp hacker that picked up code for formatting his hard drive via some random use-group advice…. You just can’t be too careful…. 😉 Yeah, this is silly… but this is what passes for understanding code snippets before plugging them in….

Anyways, flipping through the Camel book I quickly came across a similar example in the section on closures. I couldn’t find out what the weird * sigil meant even after flipping around in the index. Logically, Perl is a postmodern language. Therefore the * sigil means the same thing as it did in Kurt Vonnegut’s Breakfast of Champions. Obviously, applying the * to a scalar shoves its associated reference up Perl’s hind end. Perl’s DWIM features should cause it to psychically intuit which internal table you meant it to go to based on the type of reference you’re passing….  (Well, I figure if I can think like Larry, then learning Perl will be a lot easier.  That’s my theory, anyway.  Let’s see if this is right….)

#/usr/bin/perl
use strict;
use warnings;

sub hotwire {
  my $number = 42;
  my $string = "foo";
  my $subname = "blarney";
  no strict 'refs';
  *$subname = sub { print "Yes, it's $number and $string.\n"; };
}

hotwire();
my $number = 12;
my $string = "bar";
blarney();

Okay, this is fun…. Now, we know from the opening chapter of Intermediate Perl that you can use eval as a substitute for the type of error-catching try/catch block activity you might have seen in other languages. Of course, as in other languages, using eval for what it’s supposed to be used is generally stupid and a form of language abuse. However we feel it is our divine right to abuse whatever language we have at our disposal, so let’s just check to see that we can load evaluated text into our environment as a function:

sub loadme {
  my ($name, $code) = @_;
  no strict 'refs';
  *$name = eval $code;
}

loadme("noway", 'sub { print "yadda yadda yadda\n"; }');
noway();

Wow… it works! Woo-hoo! Okay…. Let’s try adding in our own syntactic sugar. We’ll create a function and we’ll search for a pattern and replace it with our function call:

sub check {
  my ($a, $b, $c) = @_;
  return "$a...$b...$c...";
}

sub loadme2 {
  my ($name, $code) = @_;
  $code =~ s/(\w+)~(\w+)~(\w+)/check('$1','$2','$3')/g;
  #print "$code\n";
  no strict 'refs';
  *$name = eval $code;
}

loadme2("yesway", 'sub { my $a = 99; print try~this~out, "\n", $a, "\n"; }');
yesway();

Dumb dumb dumb! There are surely better ways to accomplish this! But yeah, it’s nice to know that (in a pinch) we can do evil nasty things like this…. Let’s try one more variation that loads the text in from a file. Also, let’s store our reference in a scalar variable instead of hotwiring the package’s table of functions….

open TEXTFILE, "< loadthis";
my $file;
while (<TEXTFILE>) {
  $file .= $_;
}
close TEXTFILE;

loadme2("crazy", $file);
crazy();

sub loadme3 {
  my ($code) = @_;
  $code =~ s/(\w+)~(\w+)~(\w+)/check('$1','$2','$3')/g;
  return eval $code;
}

my $C = loadme3($file);
print "Now we'll try running the code from a scalar...\n";
$C->();

There we go!

Now… this is evil… this is wrong… this is bogus and brain dead. But but by marrying this to our table parsing code, we can create our own Frankenstein style scripting language. We already can create specialized data structures can initialize them with data parsed from a pseudo-DSL. We can customize these structures with perl code tacked on however we like. This can be basic event and override code like you see in GUI frameworks. I figure that the code is already interpreted for us into compiled-ish code when the references are read in, so you won’t see any real performance hit when you call these things– they should be indistinguishable from any other sub. Just look for things to slow down at startup as you add more and more of these to the script files. (Yeah, I know. I need to learn about hardware and assembly language– and I need to go pick up a comp sci degree, too– I’ll get around to it sometime. Right now I just want something that works….)

Back in Lisp we made the mistake of trying to create an all-new language that ran on top of Lisp. This was bad because we couldn’t take advantage of Lisp’s language features in our hacked/custom dsl-thing. (We should have gone with the grain of the language and modified Lisp slightly so that it became our lisp-ish DSL by itself.) In Blub, we hacked someone’s custom evaluator to go even further to craft our own language from scratch. This proved to be an unsustainable amount of work beyond a certain level of complexity. With Perl, we are compromising by creating special language-interfaces for initializing data structures… and then switching over to (maybe) slightly-abused Perl code where it suits. There’s better ways to do this, but this may be sufficient for a certain class of problems….

Probably what we’re doing is rolling our own Meta-object protocol. (After a fashion, anyway.) That’s not necessarily a complete waste of time: now maybe if we study it, we’ll have a use for it….

Advertisements

2 Responses to “using the sphincter-sigil to abuse Perl”

  1. draegtun Says:

    Nice title Lispy! The name your looking for is Typeglobs but sphincter-sigil might actually be an improvement 😉

    Your description of Typeglobs is correct. Simon Cozens in his book “Advanced Perl Programming” has a very good section on it. His code below helps explain typeglobs succinctly…..

    *a = \"Hello";
    *a = [ 1, 2, 3 ];
    *a = { red => 'rouge', blue => 'bleu' };
    
    print $a;          # Hello
    print $a[1];       # 2
    print $a{'red'};   # rouge
    

    Simon also goes to show how you can alias variables together using typeglobs….

    @b = (1,2,3,4);
    *a = \@b;
    push @b, 5;
    print @a;  # 12345
    
    # however
    $a = "Bye";
    $b = "Hello there!";
    print $a;   # Bye
    

    You need to understand about the Symbol Table to explain why first bit works and second bit doesn’t in the above example. Simon’s book or “Mastering Perl” by brian d foy are both excellent resources on this. brian’s book (or versions of) are available online at his website. Here’s the chapter on symbol tables and typeglobs

    /I3az/

    PS. My perl code always does strange things! Your right to always treat it with due caution and very wise to use/learn/adapt it a snippet at a time.

  2. draegtun Says:

    Re: eval

    There’s probably a good reason why eval is only one letter away from evil 😉 But used wisely it can be very powerful.

    However be wary of the differences between string & block evals.

    If you can do it in an eval block then do so because this catches compile time errors and is faster because doesn’t need recompiling each time its run unlike a string eval.

    Of course in your first example you can do it without eval by just passing a subroutine coderef…..

    sub loadme {
      my ($name, $code) = @_;
      no strict 'refs';
      *$name = $code;
    }
    
    loadme("noway", sub { print "yadda yadda yadda\n" } );
    noway();
    

    For your other examples and what your really after in previous blog you may find something useful on CPAN. Have a look at Parse::RecDescent.

    /I3az/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: