By Chris at March 9th, 2006 13:37:08

One of the things that's been bugging me lately is the lack of strong typing in perl. Sure, you start off by thinking "OMG! There's only three data structures in perl! Great!" Later you make the realisation that there are, in fact, references, and you can build complex data structures and make useful programs. Later on, you go "what's this 'bless' thingy?" and learn about perl's module encapsulation, and the dirty, unclean "implementation" of the Object Oriented paradigm.

And you are happy. You can make nice, useful programs that work, with relative ease.

Until you realise that there are bastards out there that are really slack with their coding.

One of the concepts that seems foreign to perl programming is the idea of "robustness". That is, your code works the way it says it should, and tollerates errors gracefully. One reason for this is that perl is a weakly typed language: you only have three datatypes, and the best you can do with that is use subroutine prototyping to specify that how many of each you want. This means that if you want your code to be "robust", you have to check everything yourself, in your code to make sure its sane before continuing. And you have to do it over, and over, and over. So people forget about it, or don't do it because its so hard. But the downside of that is that you make assumptions about what gets handed to you. That might be fine when there's a small, closed group of people working on the code (with good style guides and peer review, etc), but when you're in the business of providing libraries that other people use, you can't make this assumption. In essence, any interface contract that you may define is not enforceable unless you want to do the high work overhead yourself. Strongly typed languages allow for interface contract, or specification enforcement in code, because when you define a function or method, you define the contract as well.

So, in perl we can't trust our given input, and verification of it is long and tedious. What can you trust? We can write a whole bunch of routines to verify this for us, but that can get cumbersome when writing your code. You can trust your own libraries, so there should be a way to use that. So I've been trying to think of ways to easilly enforce stronger typing, easy to use, and fairly transparent.

One method that I've come across is by using Attribute Handlers. These are funky bits of code that are called during compile time when you declare a function, variable, etc. You can use them for all sorts of things, from debugging, error handling, watching, enforcing calling conventions, permissions (public/private/protected), etc.

The first prototype I made was a simple method debugging handler. The idea is: when a function is called, the call is intercepted, and information about the call is gatherred, and then logged. Then the call is executed, and the results inspected and logged, and then handed back to the callee. This is the code to do it:

package MyDebug; use strict; use Attribute::Handlers; use Data::Dumper; sub Debug :ATTR { my ($package, $symbol, $referent, $attr, $data, $phase) = @_; my $name = join '::', *{$symbol}{PACKAGE}, *{$symbol}{NAME}; no warnings 'redefine'; *{$symbol} = sub { print "DEBUG: Enterring: $name\n"; my @arr = @_; my $wantarray = wantarray; print "DEBUG: Called: $name(\n" . Dumper(@arr) ."\t)\n"; print "DEBUG: in " .($wantarray?'array':'scalar') ." context\n"; if ( $wantarray ) { my @res = $referent->(@arr); print "\nDEBUG: Returning array: (" . Dumper(@res) .")\n"; return(@res); } else { my $res = $referent->(@arr); print "\nDEBUG: Returning scalar: " . Dumper($res) ."\n"; return($res); } }; } 1;

How to use it:

#!/usr/bin/perl -w use strict; use base qw( MyDebug ); sub foo : Debug { print "LOOK AT ME I'M THE FUNCTION AND I'M DOING STUFF!\n"; } foo();

So what's it doing? Note the magic "sub foo : Debug" in there? The ": Debug" in the delaration is specifying to use the attribute handler. The handler itself (in the first block of code) accepts a bunch of information about the function. Then it overwrites the function with a function of its own, that function collects and outputs information about how it was called, and presents it to the user. It then runs the original code, and collects information about those results too. It finally hands the results back to the callee.

By adding a simple handler to a subroutine declaration, I can easilly collect useful debugging information. I could add all sorts of things, like catching die statements, warnings, etc, recording them, then propogating them. You can do similar things with variables, such as monitoring them for changes (although I've got some work to do on making that a reality). With the additional knowledge that I can pass arguments into the handler, I can see that I can start doing much more. Type checking, for example...

So I wrote this rudimentary prototype:

package MyTypes; use strict; use Attribute::Handlers; sub Type :ATTR { my ($package, $symbol, $referent, $attr, $data, $phase) = @_; my @tests = (ref($data)?@$data:$data); my $name = join '::', *{$symbol}{PACKAGE}, *{$symbol}{NAME}; no warnings 'redefine'; *{$symbol} = sub { my @arr = @_; print "TYPES: Checking types for $name\n"; print "TYPES: Check rule: ".join(', ',map { "'$_'" } @tests)."\n"; # go through each attribute, and check for it my $i=0; foreach my $atty ( @tests ) { my $pass = 0; # this is rudimentary, and can be all sorts of things # and lookup tables MAGICSPRAY: foreach my $test ( split(/\|/,$atty) ) { if ( $test eq 'undef' && not defined $arr[$i] ) { # well, that's okay then $pass = 1; last MAGICSPRAY; } # catches an extra case that UNIVERSAL::isa doesn't # (eg: ref(blah)=="") if ( defined $arr[$i] and ref($arr[$i]) eq $test ) { $pass = 1; last MAGICSPRAY; } # allows for polymorph too if ( defined $arr[$i] and UNIVERSAL::isa($arr[$i], $test) ) { $pass = 1; last MAGICSPRAY; } } if ( ! $pass ) { die("Argument $i is not valid (does not match test '$atty')"); } $i++; } return $referent->(@arr); }; } 1;

Used:

#!/usr/bin/perl -w use strict; use base qw( MyTypes ); sub foo : Type( "Foo::Bar", "|undef|SCALAR", "SCALAR", "HASH", "ARRAY" ) { print "LOOK AT ME I'M THE FUNCTION AND I'M DOING STUFF!\n"; } ... foo($foobar, "", $scalarref, {}, []);

This does something similar to the Debug handler above, but it accepts input and stores that in the generated subroutine. At runtime, when the function is executed, it checks the supplied arguments against the argument rules ("Foo::Bar", "|undef|SCALAR", "SCALAR", "HASH", "ARRAY"), and dies if it doesn't match. By using perl's inbuilt ref() and UNIVERSAL::isa methods, you can start using the trust in your code to verify input (sure, someone could bless something to "Foo::Bar" themselves, but they're being willfully negligent and thus you can argue that you don't care anymore). This is rudimentary, and so is the grammer defining the argument rules, but it works (for this simple example).

You can then see that you can do all sorts of things here, in this "pre-function" stage. I can define arbitrary data 'types', like "string_email", which checks that the argument is a string that contains a 'valid' email address. I can automatically instantiate objects if I'm only supplied an object ID, etc etc. The possibilities are endless.

And hey, look, you can stack them:

#!/usr/bin/perl -w use strict; use base qw( MyTypes ); use base qw( MyDebug ); sub foo : Debug : Type( "Foo::Bar", "|undef|SCALAR", "SCALAR", "HASH", "ARRAY" ) { print "LOOK AT ME I'M THE FUNCTION AND I'M DOING STUFF!\n"; } ... foo($foobar, "", $scalarref, {}, []);

Sure, this can be achieved inline using something like:

sub foo { @_ = Debug(Type( ["Foo::Bar", "|undef|SCALAR", "SCALAR", "HASH", "ARRAY"], @_ )); ... }

But this is way cooler, and I think its also way easier. The Debug example is a good example for doing it this way, as its much easier to achieve it using handlers instead of inline in the code. Handlers allow you powerful tools to be able to do all sorts of things that would otherwise be impossible, or very difficult to do.

( Comments Off | permalink | in Development )
By Chris at March 4th, 2006 14:18:11
Urban Ruins

While reading one of my new favourite blogs, Red Ruin, I came across a link to a photographers gallery of urban ruins in Japan. If you have any interest in architecture or photography I suggest you check them out. Red Ruin is also worth checking out, as he's all about sharing good music and good art experiences.

( Comments Off | permalink | in World )
By Chris at 14:05:42

Just after Christmas I started sharing Jono's World of Warcraft account so that I could give it a try, and play online with Michele. Up until two weeks ago, I had levelled up a Night Elf Hunter to level 24. Then, two weeks ago, we decided to form a guild and start playing regularly with some of the people at work. The problem was, Jono also wanted to play at the same time, so I finally had to buy my own account.

We're playing the Horde faction, and I'm playing a Tauren Druid. Druid's kick ass, but I'm hanging out for level 20 so I can get my cat form. After playing my Night Elf for so long, it's nice to play a different character and class. I'm thinking of creating a new character somewhere, possibly a Rogue.

By Chris at 13:36:27
iPod Remote

Hot on the heels of the last post, here's a picture of the shiney new toy I just bought for my iPod. This little gadget allows you to listen to FM radio on your iPod and control everything with a convenient wired remote. You also get a set of earphones with a shorter cable, but I prefer to use my own, clip-on earphones.

I bought the damn thing because it was getting annoying pulling the iPod out when on the bus or walking, when the volume was too loud, or the current song was crap. I went looking for remotes at the same time that Apple released these babies. Of course, it took over a month for them to finally be available here. Of course, it also being a radio is icing on the cake.

I read an article somewhere that Apple are getting much better at being secretive and surprising the market with their products. The cynic in me suggests that this is because they don't actually produce any of these new products before they launch them, so only Apple and the manufacturer exec's and pre-fabbers know anything about them. Which, of course, means that you have to wait over a month for them to make and ship the damn things before they actually turn up on the shop floors after.

( Comments Off | permalink | in Toys )
By Chris at 13:15:43

The question about this blog that's been floating around my mind is: "Why do I write in it?". It’s not as if I have anything particularly interesting to say, nor do I say it interestingly. I can't write, and I don't have anything to write about. I do tend to use it as a dumping ground for some things, but the things that I think about most will never make it here.

Having a good writing style has never been a strong point for me. I tend to write about things the same way I think about them, which seems to be ass-backwards from most people. While I know it makes sense to me (because I know my own thought processes well enough), I know that other people reading these will come along and misinterpret most of it, unless I go back and rewrite it all. That takes time, and I'm likely to get bored and not do it. The last post is a good example of this. I think it’s fairly safe to say that my rewrites need some work too.

I'm not usually happy with what I creatively produce. I can be happy with a bit of code, a technique, some action or performance, but when it comes to my creative output, I am never happy with how it turns out. Writing is no exception. The things I construct in my head look great there, but I lack the ability to transfer that into a different medium, no matter how hard I try. I think part of the problem might be that I think in circles, recursively, with thoughts feeding off and into each other, building up over time. It’s hard to translate that into to what is essentially a linear medium (which is all I really know). What's worse is that the times where I feel that I could get these out are usually the most inopportune times possible - I'm driving to work, having a shower, or out with friends, etc. Funny how that works.

So I can't say the things that I want to say. Even still, the things I want to say are either too boring, or are so complex that it would take a book to do them justice. I've tried waxing philosophical before, but I usually give up after a while as the entries become too long and cumbersome. What I usually end up posting is "OMFG, look at me, I bought an iPod", or something similar. Not exactly representative of me or my values (for instance, that screams crass materialism, but that's not what I'm about).

So why do I write anything in here? I think it eventually boils down to some retarded idea of vanity. I've had a 'web presence' for a long time. I 've traditionally used it as a storage place for things that interest me, but I felt that it should be something that other people can get some use from too (after all, I do have some interesting stuff here). So, I started a blog, to give some sort of identity to the content within. Over time it appears that the blog has taken over, and now is the purpose. The blog is the website. The reason I write here is so I can justify having a website. Vanity.

The irony is that as purpose of the website, the blog gives identity to me. This, because I don’t seem to be able to write about anything significant, is not representative of me. It's 'Chris Lite'.

Or maybe I'm just full of shit. You decide.

( Comments Off | permalink | in Rants )