in reply to conditional catch-blocks 'try {} catch(COND) { }'
G'day Rolf,
Just throwing a few ideas around.
"... catch error-objects ..."
I'll assume, if $e is the error-object, then 'ref $e' returns "TypeError", "RangeError", and so on.
"... didn't show much implementation ... more interested in comments regarding the interface ..."
I'll use the Try::Tiny syntax.
You can implement something else; e.g. there's some alternatives in the
"SEE ALSO" section of that module,
there's the experimental "Try Catch Exception Handling"
introduced in Perl v5.34, and no doubt many more.
Probably in some error handling module, you might have something like:
{
my %dispatch_error;
BEGIN {
%dispatch_error = (
TypeError => sub { ... handle TypeError exceptions ... },
RangeError => sub { ... handle RangeError exceptions ... }
+,
...,
'' => sub { ... handle any unspecified exceptions ... },
);
}
sub handle_error {
my ($e) = @_;
my $error_type = ref $e;
$error_type = '' unless exists $dispatch_error{$error_type};
$dispatch_error{$error_type}->($e);
return;
}
}
Then your try/catch code could be something like:
try {
myroutine();
}
catch {
handle_error($_);
};
So now, the interface in the main code becomes very simple.
Beyond whatever try/catch syntax you choose, you can do pretty much whatever you want with the implementation
(as long as it has a handle_error() routine).
I would assume your error objects are instantiated from classes with appropriate information;
for example, a RangeError class has min and max attributes.
Re^2: conditional catch-blocks 'try {} catch(COND) { }'
by afoken (Chancellor) on Sep 20, 2021 at 11:01 UTC
|
{
my %dispatch_error;
BEGIN {
%dispatch_error = (
TypeError => sub { ... handle TypeError exceptions ... },
RangeError => sub { ... handle RangeError exceptions ... }
+,
...,
'' => sub { ... handle any unspecified exceptions ... },
);
}
sub handle_error {
my ($e) = @_;
my $error_type = ref $e;
$error_type = '' unless exists $dispatch_error{$error_type};
$dispatch_error{$error_type}->($e);
return;
}
}
try {
myroutine();
}
catch {
handle_error($_);
};
One obvious disadvantage is that you need a different handle_error() routine for each different set of exceptions you want to handle. Also, the exception handling code moves away from the catch. And the exception handlers might be outside the scope of the try-catch-block (they aren't, in your example), so they don't have access to local variables.
How would you solve this?
# not quite Perl
sub work
{
# Handle a few exceptions
my $result1;
try {
$result1=doSomeMath();
} catch (DivByZeroException) {
$result1="div by 0";
} catch (NotANumberException) {
$result="not a number";
} # no explicit handler for other exceptions, so re-throw
# Handle a different set of exceptions
my $result2;
try {
$result2=doSomeMoreMath();
} catch (DivByZeroException) {
$result2="div by 0";
} # no check for NaN, will be re-thrown like any other Exception h
+ere
# Handle all possible exceptions, with a special handling for one
+exception
try {
writeToFile($result1,$result2);
} catch (IOException) {
say "oopsie - I/O problem";
} catch {
say "something else went wrong";
}
}
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
|
#!/usr/bin/perl
use warnings;
use strict;
use Try::Tiny;
sub handle_error {
my ($e, $dispatch_error) = @_;
my $error_type = ref $e;
$error_type = '' unless exists $dispatch_error->{$error_type};
$dispatch_error->{$error_type}->($e);
return
}
for my $case (0, 1, 2) {
try {
[ sub { die bless {source => 'Int', target => 'Float'}, 'TypeE
+rror' },
sub { die bless {value => 2**65,
min => -2**32,
max => 2**32}, 'RangeError' },
sub { die bless {}, 'Segmentation Fault' }
]->[$case]->()
} catch {
handle_error($_, {
TypeError => sub {
warn "Coercing type $_[0]{source} to $_[0]{target}" },
RangeError => sub {
warn "$_[0]{value} not between $_[0]{min} and $_[0]{ma
+x}" },
'' => sub { die $_[0] },
});
};
print "next\n";
}
maybe?
map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
| [reply] [d/l] [select] |
|
A little bit more code and indenting than expected, but yes, that should work.
Try::Tiny implements catch as a prototyped sub (sub catch (&;@)), so it should be possible to implement something like sub catchif ($&;@) that accepts an additional parameter, and to extend the logic in sub try to basically implement your handle_error() function.
That would allow the following syntax that at least looks similar to try-catch in other languages:
try {
# ...
} catchif TypeError => {
# ...
} catchif RangeError => {
# ...
} catch {
# ...
} finally {
# ...
}
I think it would not need many changes:
- sub catchif is very similar to sub catch, it "just" needs to also embed its first argument in the returned object
- try needs to build a hash of catchif cases, extracting the first argument from catchif objects
- try has to examin the hash before executing the generic catch case.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
|
|
|
|
use Try::Tiny::ByClass;
try {
die $exception_object;
} catch_case [
'Some::Class' => sub {
# handle Some::Class exceptions
},
'Exception::DivByZero' => sub {
# handle Exception::DivByZero exceptions
},
], finally {
# always do this
};
Tho I sense a bit of confusion between finally and a "naked" catch
| [reply] [d/l] [select] |
|
G'day Alexander,
As per the original question, I focussed on the interface rather than the implementation.
I did, however, leave some clues but I didn't expand upon them.
When I said "I would assume your error objects are instantiated from classes with appropriate information; for example, a RangeError class has min and max attributes.", I was thinking that myroutine() might look something like:
sub myroutine {
...
if ($n < 5 or $n > 10) {
die RangeError::->new(min => 5, max => 10, got => $n, ...);
+
}
...
}
Then a generic $dispatch_error{RangeError} routine would have sufficient information to generate an error message.
That might look something like:
ERROR: Out of bounds. The allowed range is $e->min() to $e->max() (inc
+lusive). The number received was $e->got().
The RangeError class might even have some boilerplate to create such a message using the
attribute values of the error object.
My main intent was to suggest an interface with "an easy syntax" (as requested) and to decouple the implementation.
I hadn't really given the implementation details much thought; however, what I've described above,
is roughly what had in the back of my mind.
| [reply] [d/l] [select] |
Re^2: conditional catch-blocks 'try {} catch(COND) { }'
by LanX (Saint) on Sep 20, 2021 at 13:37 UTC
|
Thanks Ken,
I agree with afoken that an uniform global handle_error doesn't solve the need to define an ad-hoc catch condition.
> there's the experimental "Try Catch Exception Handling" introduced in Perl v5.34
a great I forgot about this one, have to see if it covers all the cases.
And it's certainly not backwards compatible. Which is a general problem of most experimental features.
Having a stable strategy which works with older Perls albeit slowly and nice and fast with newer once would facilitate the development and evalution of new features tremendously.
> and no doubt many more.
It would be nice to formulize the requirements covered and to test all those "many more" modules against them.
| [reply] |
|
>> there's the experimental "Try Catch Exception Handling" introduced in Perl v5.34
> And it's certainly not backwards compatible. Which is a general problem of most experimental features. Having a stable strategy which works with older Perls albeit slowly and nice and fast with newer once would facilitate the development and evalution of new features tremendously.
Which is why there is Feature::Compat::Try (FCT), which will choose between the v5.34 native implementation or the Syntax::Keyword::Try (SKT) implementation, which will mean you can write code in such a way that it is backwards compatible down to the v5.14 that SKT supports, using the same syntax on with newer and older versions of Perl. (If I've understood the docs correctly, SKT actually has additional syntax that isn't in the v5.34 native implementation, but the FCT wrapper only exposes the v5.34-compatible syntax. I think.)
Caveat: I haven't used that pair, because I am often on systems that only has v5.8, so I don't want to get in the habit of using a try/catch pair that's not compatible back that far. But I remembered having seen the FCT/SKT pair mentioned in some other recent thread, so it stuck in my head. So if v5.14 is backward-compatible enough for you, looking at FCT would be a good idea.
| [reply] |
|
use Syntax::Keyword::Try;
sub foo {
try {
attempt_a_thing();
return "success";
}
catch ($e) {
warn "It failed - $e";
return "failure";
}
}
To avoid reusing/redefining a special variable like Try::Tiny does with $_ ?
We already have $@
Or is there another reason I'm not aware of?
| [reply] [d/l] [select] |
|
> Which is why there is Feature::Compat::Try (FCT)
So Syntax::Keyword::Try is an XS module for exactly back-porting this feature "try" till v5.14?
> So if v5.14 is backward-compatible enough for you, looking at FCT would be a good idea.
After Devel::Declare and family, I'm no big fan of XS dependencies hacking into the parser.
The less => the more stable => the better.
> I haven't used that pair, because I am often on systems that only has v5.8,
Using pure Perl for backwards compatibility wouldn't have such limitations.
OTOH using XS would be faster on "older" Perls, at least when parsing.
| [reply] [d/l] |
|
"I agree with afoken that an uniform global handle_error doesn't solve the need to define an ad-hoc catch condition."
My response may (or may not) alter your view on that.
If it's not suitable for your needs, that's fine: as I said, "Just throwing a few ideas around.".
On the try/catch in 5.34, I generally don't use or suggest experimental features;
in fact, I normally only mention them when recommending not to use them.
In this instance, you had said, "... new syntax ... experimentally implemented ...";
accordingly, I pointed out a core experimental feature
(in the certain knowledge that you're smart enough to understand the pitfalls)
— I actually don't know if what you're doing is purely academic or for a production system.
| [reply] |
|
|