Monday, September 21, 2009

Exception Handling: Part 03 of 05

Part 3: Post-Modern Exception Handling with Classes

Target Audience: This pitch would interest a developer interested in the Exception Handling Best Practices in Perl. This series starts with basic exception handling and adds complexity, ending with pragmatic Best Practices recommendation.

I have never met Larry Wall, the inventor of Perl. I don't know what he is like as a person. However, I like the approach he brings to the programming world. Specifically, I like that his approach is to lighten up programming in areas where there is no reason to be grim. While sharpening focus on that which really deserves the attention of a creative developer.

In a sense, Larry's Perl is like English - a bitch that isn't faithful to any one narrowly-defined culture, religion, nation or community but goes around mixing promiscuously, taking attractive elements from here and there, whatever works, to become more agreeable and less strait-laced. There are people who find English a terrible language. Those are the kind of people who probably don't find Perl endearing. Those are also probably the kind of guys (and girls) who take notes in hard-bound books in neat handwriting with pagination and time-date stamps. As for me, Perl is my kind of bitch.

The post-modern approach isn't hide-bound. It doesn't prize originality over all else and is accepting of human foibles. One can be rather ingenious in putting together ideas freely and flexibly. And that is where I'd rather invest my time than spend a lot of it reinventing the wheel and unimaginative book-keeping.

Onward, onto Exception Classes, with this code-snippet.



use Exception::Class (
'My::Exception::Base',
'My::Exception::Base::Outer' => {
isa => 'My::Exception::Base',
description => 'Error outside',
},
'My::Exception::Base::Outer::Inner' => {
isa => 'My::Exception::Base::Outer',
description => 'Error inside',
},
);

What if I needn't write the 'throw' sub each time? What if I could leave it to .. say a Class. I could then dervive other classes from the base class to customize behavior. These specialized classes would inherit all methods of the base class, including the handy 'throw' method.

Hmm, all the goodness of object-orientated programming, at the finger-tips..

That is exactly what Exception::Class does! Now are you convinced?

What I've done is I have defined a hierarchy of exception-handling classes from the base class. In case you still need convincing, now I can query an exception's class and handle the exception accordingly.

An exception is thrown as:

My::Exception::Base->throw ("Unable to fopen $fname");

and handled as:

eval {
$secret = outer($fname);
};
if ($@) {
print $@->trace;
}

for obtaining a stack-trace.

Want to dispatch handlers based on the class type? You can do that now! Follow the simple querying patter illustrated in the snippet:

eval {
$secret = outer($fname);
};
if ($@->isa('My::Exception::Base::Outer::Inner')) {
print $@->trace;
}

Note how easy stack-tracing is with the 'trace' method. There are other methods of the base Exception::Class which are just as useful.

But hang on before you break out that bottle of bubbly! All your exception-handling woes are fast disappearing.. but there is still one niggling little problem. That problem comes from classes. Can you guess?

Remember, while your code is now (nearly) fit for exhibition in any museum of post-modern art, not everyone else's code is so! Particularly, I direct your attention to all those modules from CPAN, judging by all the 'use' directives your code is liberally peppered with. What about those modules? - if any one of those doesn't implement exception-handling based on 'Exception::Class', that breaks our model. Can you see why?

That little bit takes us to $SIG{__DIE__} in the penultimate part of this series. Here, now, is the complete code. What is the handler of all 'die' calls upto? That answer is coming Part 4.

use strict;
$, = "\t", $\ = "\n";
use Exception::Class (
'My::Exception::Base',
'My::Exception::Base::Outer' => {
isa => 'My::Exception::Base',
description => 'Error outside',
},
'My::Exception::Base::Outer::Inner' => {
isa => 'My::Exception::Base::Outer',
description => 'Error inside',
},
);

sub main {
my $fname = 'secret.dat';
my $secret;

$secret = outer($fname);

print $secret;
};

eval {
main();
};
if ($@) {
print $@->trace;
}

sub outer {
my $fname = shift;
my $secret;
$secret = inner1($fname);
return $secret;
}

sub inner1 {
my $fname = shift;
my $secret;
$secret = inner2($fname);
return $secret;
}

sub inner2 {
my $fname = shift;
my $secret;
$secret = inner3($fname);
return $secret;
}

sub inner3 {
#My::Exception::Base->throw ("junk");
my $fname = shift;
my $secret;
open(SECRET, "<$fname")
|| My::Exception::Base->throw ("Unable to fopen $fname");
$secret = < SECRET >;
close(SECRET);

return $secret;
}

local $SIG{__DIE__} = sub {
my $e = shift;
if ($e->isa('My::Exception::Base')) {
die $e;
} else {
die My::Exception::Base->new($e);
}
};

Exception Handling: Part 02 of 05

Part 2: Stack-tracing with a custom 'throw' sub.

Target Audience: This pitch would interest a developer interested in the Exception Handling Best Practices in Perl. This series starts with basic exception handling and adds complexity, ending with pragmatic Best Practices recommendation.


So you've come back wanting to put a stack-trace in your code. Here's the code, before we start deconstructing. Add this sub throw to your code and call it 'myLittleException01.pl'.


sub throw {
my $mess = shift;
my $i = 1;
package DB;
while (my @parts = caller($i++)) {
print "$parts[3](@DB::args), $parts[1], $parts[2]";
};
die $mess;
}


Modify sub 'inner3' to 'throw' instead of 'die' as illustrated:


sub inner3 {
# throw ("junk");
my $fname = shift;
my $secret;
open(SECRET, "<$fname") || throw ("Unable to fopen $fname");
$secret = < SECRET >;
close(SECRET);

return $secret;
}


Note, you can 'throw' just as well as 'die' anywhere in your code.

Before go any further, proof of the pudding is in the eating. Here's what a stack-trace looks like.



There are actually three stack-traces here - did you notice? That's because my main package executes the functions as follows:


# F i r s t #
eval {
$secret = outer($fname, "1");
};
if ($@) {
print $@;
}

# S e c o n d #
eval {
$secret = inner2($fname);
};
if ($@) {
print $@;
}

# T h i r d #
$secret = inner1($fname);


Why am I doing this? To demonstrate that this is indeed genuine stack-tracing. Bear with me if it seems redundant.

Now for the catechism.

Follow me! (Cont'd)

8.
STACK TRACE
What is it? How does it benefit?
Run the script.
Track the programmatic flow via stack-trace.
10.
sub 'throw' - what is the minimum definition? ('die', with message)

sub throw {
my $mess = shift;
die $mess;
}

11.
use 'caller' for STACK TRACE
What is the frame-stack? (Code executes in frames. Like shooting pictures with a camera in rapid succession to execute a movie.)
Is a function in Perl self-aware? (Can it introspect?)
Does a function know what frames were executed before it?
How to obtain a frame-stack?
perldoc caller
Try the following: put in sub 'caller' and observe behavior.
What do you see?

sub throw {
my $mess = shift;
my $i = 1;
while (my @parts = caller($i++)) {
print "$parts[1], $parts[2], $parts[3]";
};
die $mess;
}

12.
Use 'caller' for STACK TRACE (cont'd).
perldoc caller - in case you missed it first time! :o)
Try the following: refine 'caller' behavior.
What do you see?

sub throw {
my $mess = shift;
my $i = 1;
while (my @parts = caller($i++)) {
print "$parts[3](), $parts[1], $parts[2]";
};
die $mess;
}

13.
Try the following:
Uncomment in 'inner3' - throw("junk")
Call 'inner3' in main without the 'eval' block;
What do you see?
14.
To see the arguments of a function call.
perldoc DB
package DB

sub throw {
my $mess = shift;
my $i = 1;
package DB;
while (my @parts = caller($i++)) {
print "$parts[3](@DB::args), $parts[1], $parts[2]";
};
die $mess;
}

15.
The final script is provided below.

use strict;
$, = "\t", $\ = "\n";

my $fname = 'secret1.dat';
my $secret;

eval {
$secret = outer($fname, "1");
};

if ($@) {
print $@;
}
eval {
$secret = inner2($fname);
};
if ($@) {
print $@;
}
$secret = inner1($fname);

print $secret;

sub outer {
my $fname = shift;
my $secret;
$secret = inner1($fname);
return $secret;
}

sub inner1 {
my $fname = shift;
my $secret;
$secret = inner2($fname);
return $secret;
}

sub inner2 {
my $fname = shift;
my $secret;
$secret = inner3($fname);
return $secret;
}

sub inner3 {
# throw ("junk");
my $fname = shift;
my $secret;
open(SECRET, "<$fname") || throw ("Unable to fopen $fname");
$secret = < SECRET >;
close(SECRET);

return $secret;
}

sub throw {
my $mess = shift;
my $i = 1;
package DB;
while (my @parts = caller($i++)) {
print "$parts[3](@DB::args), $parts[1], $parts[2]";
};
die $mess;
}


What have we achieved? Quite a bit, actually! We are able to attain a stack-trace and that itself is of enormous value in debugging. But before we get too excited, what are some the potential drawbacks?

Aha! I still depend on strings for error-handling. How would you like to improve the world a little bit further? Did I hear you say "Exception Classes?" That's right. Ladies and Gentlemen, I give you, Exception::Class (Sound of furious clapping and oohs and aahs as the public goes wild in anticipation.)

That's what's coming up in Part-03.

Exception Handling: Part 01 of 05

Part 1: Exception Handing Basics

Prelude

What better place to begin than one of the hottest areas of software development - exception handling! Just kidding, folks :o)

When I was a novice writing software, my supervisors had to force us newbies to put in exception handling. We thought of them as old fogies, with nothing better to do than make life miserable for us young people brimming with creativity and talent.

Life goes by and pretty soon one becomes an old fogey oneself. I haven't quite gotten there myself (yet), but I recently had the opportunity to revisit exception handling when some expensive code died without a stack-trace and I couldn't leave for the day until I got to the bottom of it. Unfortunately the entire application, developed by an out-sourcing firm, had been designed by some brilliant programmers who paid only a cursory nod to exception handling. That pretty much meant looking for a needle in a haystack everytime the code broke. Hacking into code. Putting in print statements. Uggh! (Now I realize why we need a a team of full-time experts dedicated exclusively to code maintenance.)

Thus begins the Exception Handling saga. In these 4 parts, I shall cover Exception handling in Perl from basic to best practices. Although this is a perl tutorial, you may find the concepts map to Java or any other programming language designed to make life interesting.

We'll start with the script (below). It opens a file 'secret.dat' which has but one word - the secret word. To read this secret word, the (default) main package has function 'outer' which calls 'inner1' which calls 'inner2' which calls 'inner3' where the file is opened with the native 'open' function.

Copy-Paste the script below into a file, say 'myLittleException01.pl'. The way this works is, you work through the catechism using the accompanying script in each of the parts 1 through 4 (See: "Follow me!" below). There, you're all set.


use strict;
$, = "\t", $\ = "\n";

my $fname = 'secret.dat';
my $secret;
eval {
$secret = outer($fname);
};
if ($@) {
print "Got e: $@";
}

print $secret;

sub outer {
my $fname = shift;
my $secret;
$secret = inner1($fname);
return $secret;
}

sub inner1 {
my $fname = shift;
my $secret;
$secret = inner2($fname);
return $secret;
}

sub inner2 {
my $fname = shift;
my $secret;
$secret = inner3($fname);
return $secret;
}

sub inner3 {
die ("junk");
my $fname = shift;
my $secret;
open(SECRET, "<$fname") || die ("Unable to open $fname");
$secret = < SECRET >;
close(SECRET);
return $secret;
}



Follow me!

1.
No exception handling:
Run the script without any exception handling;
Modify 'inner3' - remove 'die'.
2.
How to throw an exception?
In perl, 'die'; See 'inner3'.
3.
Now with 'die' in sub 'inner3' - run-time error, script exits with error message
4.
How to trap the exception for handling?
'eval' block and special variable '$@'
The error is trapped and message displayed (i.e. exception handled).
Note the use of special variable '$@'.
5.
What happens with two instances of 'die'?
Only the 1st exception is handled. Obvious?
7.
Drawbacks?
- In any software application, 'die' will be thrown from many places.
- STACK TRACE: How to trace through the maze of callers?
- - The application is a tree data-structure (DAG)
- - $@ - string-based exception-handling scheme
- - Exception handling by 'eval' block and $@ is limited.

You can see what basic facilities Perl offers for exception handling. And also how string-based exception handling is limited when it comes to software applications with a maze of interacting packages and external dependencies. There is no stack tracing which makes debugging a hit-and-miss. The sort of thing that keeps developers away from the arms of pretty girls (or boys, depending on gender and sexual orientation) and hastens the onset of burn-out.

Now let's make the world a little better place by putting in stack tracing. And since, ladies and gentlemen, we are fine programmers, let us first devise this stack-tracing contrivance ourselves.

The woods are lovely dark and deep ..

A few introductory words are in order.

This blog presents live examples with a practical flavor from the deep, dark and lovely woods of the software world.

Programming is a form of self-expression. I believe this to be the correct attitude to software development at all levels from scripting and architecture to project management and business development in the software world. As one develops in this path, one finds oneself using a programming language as a tool to express increasingly powerful ideas encapsulating growing sophistication. One naturally relies on increasingly sophisticated programming constructs - advancing from shaky scripts with patchy logic glued together with basic control structures to rock-solid organization and reusability with packages and namespaces and finally composite design patterns in object-oriented idioms deploying all the worldly goodness of abstraction, encapsulation, composition and inheritance.

It is thrilling to find programming languages on an evolutionary path leading to greater flexibility and fluent expression of ideas in code.

Thus C++ has evolved smart-pointers, templated classes and the awesome goodness of the Standard Template Library (STL).

Perl, my favorite linguistic programming language, evolves with a suave and post-modern framework - Moose, making Object-Oriented Programming a Zen experience. Zen, as in natural and intuitive. Or Catalyst - a thoroughly contemporary, industrial-strength Model-View-Controller Framework.

Life, just gets better!

In this blog, I share some of my experiences in programming in the Linux-Apache-MySQL-Perl and R (LAMP-R) as best-practices. It is a damn shame that India is still overwhelmingly biased towards expensive, closed-source technologies whereas the rich West has absorbed the economical benefits and effectiveness of open-source. Not unlike the mother from a slum feeding her child Nestle Cerelac baby-food because it comes in a tin whereas the rich mother breast-feeds her baby because she is better-informed about child-care.

Well, as a wise old man said, we must be the change we hope to see :)