Exception handling in perl is currenlty far from perfect. eval leaves a lot to be desired. Lots of {dark,cpan} code is rife with pottential code that could interfere with $@
TryCatch (on CPAN now) fixes this through the magic of Devel::Declare.
Breifly address what's wrong with eval, what I/we'd like, and then finally how it was done using Devel::Declare
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
Catch Me If You Can: Sugary Exception Handling in Perl
1. EXCEPTION HANDLING IN PERL
catch me if you can
YAPC EU 2009 = ASH BERLIN ‹ %DEVEL DECLARE / SCOPE UPPER ‹
eASH BERLIN aASH BERLIN ½ MST < FLORIAN RAGWITZ / VINCENT PIT
£MARK FOWLER / THE O’REILLY CAMEL
Tuesday, 11 August 2009
2. Catch Me If You Can:
Sugary exception handling with TryCatch.pm
Ash Berlin
YAPC::EU 2009 – Lison
A.K.A The please don’t sue me Title
Tuesday, 11 August 2009
3. Perl Sucks!
(and what to do about it)
Image courtesy of Mark Fowler
YAPC::EU 2007 – Vienna
Tuesday, 11 August 2009
4. Perl Sucks?
• Of course it does, it’s software
• Installing Modules is hard
• Perl programs are just scripts
• Its Exception handling is hateful
Tuesday, 11 August 2009
7. eval sucks
• Sucky syntax
• Easy to get (subtly) wrong.
• return doesn’t work as expected
• eval BLOCK vs eval EXPR
• $@ is global
Tuesday, 11 August 2009
8. “But Perl already has
exception handling”
Lots of Exception::* and Error::* modules
Tuesday, 11 August 2009
9. None of them Perfect
• Some are just an exception framework
• Good; doesn’t solve catching problem
• Catching errors shouldn’t be hard
Tuesday, 11 August 2009
10. I want the Moon on a Stick
Tuesday, 11 August 2009
11. I want the Moon on a Stick
Tuesday, 11 August 2009
12. Exceptions in JavaScript
try {
someCallThatDies();
}
catch (e if e instanceof Error) {}
catch (f if f.length < 10) {}
catch (g) { /* otherwise */ }
Tuesday, 11 August 2009
13. The same in Perl
eval {
some_call_that_dies();
};
if (blessed($@) && $@->isa(‘Error’)) {
my $e = $@;
}
elsif (len($@) < 10 ) {
my $f = $@;
}
elsif ($@) {
my $g = $@;
}
Tuesday, 11 August 2009
14. The same in Perl
Done properly
{
local $@;
eval {
some_call_that_dies();
};
if (my $_e = $@) {
if (blessed($_e) && $_e->isa(‘Error’)) {
my $e = $_e;
}
elsif (len($_e) < 10 ) {
my $f = $_e;
}
else {
my $g = $_e;
}
}
}
Tuesday, 11 August 2009
15. That’s a lot to
write every time
Not very DRY
Tuesday, 11 August 2009
16. Using Error.pm
use Error qw(:try);
try { some_call_that_dies(); }
catch Error with {
my $e = shift;
}
Looking good so far…
Tuesday, 11 August 2009
17. Using Error.pm
use Error qw(:try);
try { some_call_that_dies(); }
catch Error with {
my $e = shift;
}
otherwise {
my $f = shift;
if (length($f) < 10) {
}
else {
my $g = $f;
}
} Less good :(
Tuesday, 11 August 2009
18. Problems with Error.pm
• (Not to single it out as bad)
• Its Exception Object class
• AND try/catch implementations
• sub {} closures are slow
• Speed for error free path is important
Tuesday, 11 August 2009
19. TryCatch.pm
use TryCatch;
try {
some_call_that_dies();
}
catch (Error $e) {}
catch ($f where {length < 10}) {}
catch ($g) {} # otherwise
Tuesday, 11 August 2009
20. TryCatch syntax
• try must be followed by a block
• Followed by zero or more catch clauses
• catch may be followed by a signature
• catch must also be followed by a block
Tuesday, 11 August 2009
21. Try syntax
• try keyword
• Followed by a block
• That was easy :)
Tuesday, 11 August 2009
22. Catch syntax
• catch keyword
• Followed by an optional signature:
(MyError $e where { } )
• Followed by a block.
Tuesday, 11 August 2009
23. Catch Sig syntax
(MyError $e where { } )
• MyError is any valid Moose type constraint
• Optional
• Uses MooseX::Types for preference
• Else falls back to Moose’s string parsing
• Never quoted, always bare
• Example:
Str|ArrayRef[MyError]
Tuesday, 11 August 2009
24. Catch Sig syntax
(MyError $e where { } )
• $e is the variable name for the error
• Will be created as “my $e” in the block
• A variable name is required
Tuesday, 11 August 2009
25. Catch Sig syntax
(MyError $e where { } )
• Constraint (not type constraint)
• $_ is the error being tested.
• Much like grep or map, return truthy value
• PPI used to just get ‘everything in {}’
Tuesday, 11 August 2009
26. Syntax Notes
• try can be nested as deep as you like
• You can use return to exit the sub.
• No finally.Yet. It’s on my TODO
Tuesday, 11 August 2009
27. TryCatch example
sub foo {
try {
die MyError->new($_[0])
if $_[1] eq “class”;
die $_[0]
if $_[1] eq “plain”;
return “value”;
}
catch (MyError $e) { }
catch ($e where { /No Cheese!/ }) { }
catch ($e) { } # otherwise
}
Tuesday, 11 August 2009
28. TryCatch example
sub foo { This will return a value from
try { foo, not just the try/eval
die MyError->new($_[0])
if $_[1] eq “class”; i.e. use return natually
die $_[0]
if $_[1] eq “plain”;
return “value”;
}
catch (MyError $e) { }
catch ($e where { /No Cheese!/ }) { }
catch ($e) { } # otherwise
}
Tuesday, 11 August 2009
29. TryCatch example
sub foo { Moose type constraints
try { handle the “is this a blessed
die MyError->new($_[0]) object” and similar checks
if $_[1] eq “class”;
die $_[0]
if $_[1] eq “plain”;
return “value”;
}
catch (MyError $e) { }
catch ($e where { /No Cheese!/ }) { }
catch ($e) { } # otherwise
}
Tuesday, 11 August 2009
30. TryCatch example
sub foo { If this wasn’t here, and the
try { plain error was thrown, it
die MyError->new($_[0]) would get re-thrown.
if $_[1] eq “class”;
die $_[0]
if $_[1] eq “plain”;
return “value”;
}
catch (MyError $e) { }
catch ($e where { /No Cheese!/ }) { }
catch ($e) { } # otherwise
}
Tuesday, 11 August 2009
31. xkcd.com
Implementation
Here Be (anthropomorphic) Dragons
Tuesday, 11 August 2009
32. Source Filters
• Have to look through the entire source
• And only change the bits they want.
• Perl is notoriously hard to parse
• Can cause odd bugs:
Tuesday, 11 August 2009
33. Source Filters
package ShootMeInTheHead;
use Moose;
use Switch;
sub foo {
my ($variable) = @_;
return $variable + 1;
}
sub bar {
my ($variable) = @_;
return $variab1e + 1;
Global symbol "$variab1e" requires
} explicit package name at line 18.
}
Tuesday, 11 August 2009
34. Source Filters
package ShootMeInTheHead;
use Moose;
use Switch;
# load switch statement
sub foo {
my ($variable) = @_;
return $variable + 1;
}
sub bar {
my ($variable) = @_;
return $variab1e + 1;
Global symbol "$variab1e" requires
} explicit package name at line 18.
}
Tuesday, 11 August 2009
35. Source Filters
package ShootMeInTheHead;
use Moose;
use Switch;
# load switch statement
sub foo {
my ($variable) = @_;
return $variable + 1;
}
sub bar {
my ($variable) = @_;
return $variab1e + 1;
Global symbol "$variab1e" requires
} explicit package name at line 14.
}
Tuesday, 11 August 2009
36. Devel::Declare
• Lets you change the source Perl is about to
compile
• Like a source filter
• But far less fragile.
• Key-hole source filter effect.
Tuesday, 11 August 2009
37. Using Devel::Declare
• When perl parses
catch ($e) { my $foo = …
• Sees catch as a OP_CONST
Tuesday, 11 August 2009
38. Using Devel::Declare
• When perl parses
catch ($e) { my $foo = …
• Calls PL_check[OP_CONST]
Tuesday, 11 August 2009
39. Using Devel::Declare
• When perl parses
catch ($e) { my $foo = …
• Calls PL_check[OP_CONST]
• Devel::Declare hooks this
• And calls back into perl code
Tuesday, 11 August 2009
40. Using Devel::Declare
• When perl parses
catch ($e) { my $foo = …
• Calls PL_check[OP_CONST]
• Devel::Declare lets us change this into…
catch; { if (my $e = $@) { my $foo = …
Tuesday, 11 August 2009
41. Using Devel::Declare
• TryCatch doesn’t quite produce that code
• But it shows how things work
• Also uses B::Hooks::OP::{Check,PPaddr} to
solve the return problem
Tuesday, 11 August 2009
42. The return problem
• Simple:
sub foo { eval { return $val }; 1}
• Would be nice if it returned from foo
• vincent++ # Scope::Upper
• unwind HERE, @values;
Tuesday, 11 August 2009
43. The return problem
• Typing return would be better than unwind
• Install an extra PL_check hook on
OP_RETURN
• And install a custom op handler
• Makes return inside try behave like unwind
Tuesday, 11 August 2009
45. use TryCatch;
try {
some_call_that_dies();
}
catch (Error $e) {}
catch ($f where {length < 10}) {}
catch ($g) {} # otherwise
Tuesday, 11 August 2009
46. use TryCatch;
try ;{
local $@;
eval {
some_call_that_dies();
};
$TryCatch::Error = $@;
}
if ($TryCatch::Error) {
if (TryCatch->check_tc('Error')){
my $e = $TryCatch::Error;
}
elsif (do {local $_ = $TryCatch::Error; length($_) < 10 }) {
my $f = $TryCatch::Error;
}
elsif (1){
my $g = $TryCatch::Error;
} # otherwise
else {
$@ = $TryCatch::Error; die
}
}
Tuesday, 11 August 2009
47. hu∙bris
noun
excessive pride or self-confidence.
• (in Greek tragedy) excessive pride toward or defiance of the gods,
leading to nemesis.
DERIVATIVES
hu∙bris∙tic adjective
ORIGIN Greek.
Ash Berlin
<ash@cpan.org>
Questions?
Tuesday, 11 August 2009