Looking over my Raku code, I've realized that I pretty much never use CATCH blocks to actually catch/handle error. Instead, I handle errors with try blocks and testing for undefined values; the only thing I use CATCH blocks for is to log errors differently. I don't seem to be alone in this habit – looking at the CATCH blocks in the Raku docs, pretty much none of them handle the error in any sense beyond printing a message. (The same is true of most of the CATCH blocks in Rakudo.).
Nevertheless, I'd like to better understand how to use CATCH blocks. Let me work through a few example functions, all of which are based on the following basic idea:
sub might-die($n) { $n %% 2 ?? 'lives' !! die 418 }
Now, as I've said, I'd normally use this function with something like
say try { might-die(3) } // 'default';
But I'd like to avoid that here and use CATCH blocks inside the function. My first instinct is to write
sub might-die1($n) {
$n %% 2 ?? 'lives' !! die 418
CATCH { default { 'default' }}
}
But this not only doesn't work, it also (very helpfully!) doesn't even compile. Apparently, the CATCH block is not removed from the control flow (as I would have thought). Thus, that block, rather than the ternary expression, is the last statement in the function. Ok, fair enough. How about this:
sub might-die2($n) {
ln1: CATCH { default { 'default' }}
ln2: $n %% 2 ?? 'lives' !! die 418
}
(those line numbers are Lables. Yes, it's valid Raku and, yes, they're useless here. But SO doesn't give line numbers, and I wanted some.)
This at least compiles, but it doesn't do what I mean.
say might-die2(3); # OUTPUT: «Nil»
To DWIM, I can change this to
sub might-die3($n) {
ln1: CATCH { default { return 'default' }}
ln2: $n %% 2 ?? 'lives' !! die 418
}
say might-die3(3); # OUTPUT: «'default'»
What these two reveal is that the result of the CATCH block is not, as I'd hopped, being inserted into control flow where the exception occurred. Instead, the exception is causing control flow to jump to the CATCH block for the enclosing scope. It's as though we'd written (in an alternate universe where Raku has a GOTO operator [EDIT: or maybe not that alternate of a universe, since we apparently have a NYI goto method. Learn something new every day…]
sub might-die4($n) {
ln0: GOTO ln2;
ln1: return 'default';
ln2: $n %% 2 ?? 'lives' !! GOTO ln1;
}
I realize that some critics of exceptions say that they can reduce to GOTO statements, but this seems to be carrying things a bit far.
I could (mostly) avoid emulating GOTO with the .resume method, but I can't do it the way I'd like to. Specifically, I can't write:
sub might-die5($n) {
ln1: CATCH { default { .resume('default') }}
ln2: $n %% 2 ?? 'lives' !! die 418
}
Because .resume doesn't take an argument. I can write
sub might-die6($n) {
ln1: CATCH { default { .resume }}
ln2: $n %% 2 ?? 'lives' !! do { die 418; 'default' }
}
say might-die6 3; # OUTPUT: «'default'»
This works, at least in this particular example. But I can't help feeling that it's more of a hack than an actual solution and that it wouldn't generalize well. Indeed, I can't help feeling that I'm missing some larger insight behind error handling in Raku that would make all of this fit together better. (Maybe because I've spent too much time programming in languages that handle errors without exceptions?) I would appreciate any insight into how to write the above code in idiomatic Raku. Is one of the approaches above basically correct? Is there a different approach I haven't considered? And is there a larger insight about error handling that I'm missing in all of this?
"Larger insight about error handling"
Is one of the approaches [in my question] basically correct?
Yes. In the general case, use features like try and if, not CATCH.
Is there a different approach I haven't considered?
Here's a brand new one: catch. I invented the first version of it a few weeks ago, and now your question has prompted me to reimagine it. I'm pretty happy with how it's now settled; I'd appreciate readers' feedback about it.
is there a larger insight about error handling that I'm missing in all of this?
I'll discuss some of my thoughts at the end of this answer.
But let's now go through your points in the order you wrote them.
KISS
I pretty much never use CATCH blocks to actually catch/handle error.
Me neither.
Instead, I handle errors with try blocks and testing for undefined values
That's more like it.
Logging errors with a catchall CATCH
the only thing I use CATCH blocks for is to log errors differently.
Right. A judiciously located catchall. This is a use case for which I'd say CATCH is a good fit.
The doc
looking at the CATCH blocks in the Raku docs, pretty much none of them handle the error in any sense beyond printing a message.
If the doc is misleading about:
The limits of the capabilities and applicability of CATCH / CONTROL blocks; and/or
The alternatives; and/or
What's idiomatic (which imo is not use of CATCH for code where try is more appropriate (and now my new catch function too?)).
then that would be unfortunate.
CATCH blocks in the Rakudo compiler source
(The same is true of most of the CATCH blocks in Rakudo.).
At a guess those will be judiciously placed catchalls. Placing one just before the callstack runs out, to specify default exception handling (as either a warning plus .resume, or a die or similar), seems reasonable to me. Is that what they all are?
Why are phasers statements?
sub might-die1($n) {
$n %% 2 ?? 'lives' !! die 418
CATCH { default { 'default' }}
}
this not only doesn't work, it also (very helpfully!) doesn't even compile.
.oO ( Well that's because you forgot a semi-colon at the end of the first statement )
(I would have thought ... the CATCH block [would have been] removed from the control flow)
Join the club. Others have expressed related sentiments in filed bugs, and SO Q's and A's. I used to think the current situation was wrong in the same way you express. I think I could now easily be persuaded by either side of the argument -- but jnthn's view would be decisive for me.
Quoting the doc:
A phaser block is just a trait of the closure containing it, and is automatically called at the appropriate moment.
That suggests that a phaser is not a statement, at least not in an ordinary sense and would, one might presume, be removed from ordinary control flow.
But returning to the doc:
Phasers [may] have a runtime value, and if evaluated [in a] surrounding expression, they simply save their result for use in the expression ... when the rest of the expression is evaluated.
That suggests that they can have a value in an ordinary control flow sense.
Perhaps the rationale for not removing phasers from holding their place in ordinary control flow, and instead evaluating to Nil if they don't otherwise return a value, is something like:
Phasers like INIT do return values. The compiler could insist that one assigns their result to a variable and then explicitly returns that variable. But that would be very un Raku-ish.
Raku philosophy is that, in general, the dev tells the compiler what to do or not do, not the other way around. A phaser is a statement. If you put a statement at the end, then you want it to be the value returned by its enclosing block. (Even if it's Nil.)
Still, overall, I'm with you in the following sense:
It seems natural to think that ordinary control flow does not include phasers that do not return a value. Why should it?
It seems IWBNI the compiler at least warned if it saw a non-value-returning phaser used as the last statement of a block that contains other value-returning statements.
Why don't CATCH blocks return/inject a value?
Ok, fair enough. How about this:
sub might-die2($n) {
ln1: CATCH { default { 'default' }}
ln2: $n %% 2 ?? 'lives' !! die 418
}
say might-die2(3); # OUTPUT: «Nil»
As discussed above, many phasers, including the exception handling ones, are statements that do not return values.
I think one could reasonably have expected that:
CATCH phasers would return a value. But they don't. I vaguely recall jnthn already explaining why here on SO; I'll leave hunting that down as an exercise for readers. Or, conversely:
The compiler would warn that a phaser that did not return a value was placed somewhere a returned value was probably intended.
It's as though we'd written ... a GOTO operator
Raku(do) isn't just doing an unstructured jump.
(Otherwise .resume wouldn't work.)
this seems to be carrying things a bit far
I agree, you are carrying things a bit too far. :P
.resume
Resumable exceptions certainly aren't something I've found myself reaching for in Raku. I don't think I've used them in "userspace" code at all yet.
(from jnthn's answer to When would I want to resume a Raku exception?.)
.resume doesn't take an argument
Right. It just resumes execution at the statement after the one that led to an exception being thrown. .resume does not alter the result of the failed statement.
Even if a CATCH block tries to intervene, it won't be able to do so in a simple, self-contained fashion, by setting the value of a variable whose assignment has thrown an exception, and then .resumeing. cf Should this Raku CATCH block be able to change variables in the lexical scope?.
(I tried several CATCH related approaches before concluding that just using try was the way to go for the body of the catch function I linked at the start. If you haven't already looked at the catch code, I recommend you do.)
Further tidbits about CATCH blocks
They're a bit fraught for a couple reasons. One is what seems to be deliberate limits of their intended capability and applicability. Another is bugs. Consider, for example:
My answer to SO CATCH and throw in custom exception
Rakudo issue: Missing return value from do when calling .resume and CATCH is the last statement in a block
Rakudo issue: return-ing out of a block and LEAVE phaser (“identity”‽)
Larger insight about error handling
is there a larger insight about error handling that I'm missing in all of this?
Perhaps. I think you already know most of it well, but:
KISS #1 You've handled errors without exceptions in other PLs. It worked. You've done it in Raku. It works. Use exceptions only when you need or want to use them. For most code, you won't.
KISS #2 Ignoring some native type use cases, almost all results can be expressed as valid or not valid, without leading to the semi-predicate problem, using simple combinations of the following Raku Truth value that provide ergonomic ways to discern between non-error values and errors:
Conditionals: if, while, try, //, et al
Predicates: .so, .defined, .DEFINITE, et al
Values/types: Nil, Failures, zero length composite data structures, :D vs :U type constraints, et al
Sticking with error exceptions, some points I think worth considering:
One of the use cases for Raku error exceptions is to cover the same ground as exceptions in, say, Haskell. These are scenarios in which handling them as values isn't the right solution (or, in Raku, might not be).
Other PLs support exceptions. One of Raku's superpowers is being able to interoperate with all other PLs. Ergo it supports exceptions if for no other reason than to enable correct interoperation.
Raku includes the notion of a Failure, a delayed exception. The idea is you can get the best of both worlds. Handled with due care, a Failure is just an error value. Handled carelessly, it blows up like a regular exception.
More generally, all of Raku's features are designed to work together to provide convenient but high quality error handling that supports all of the following coding scenarios:
Fast coding. Prototyping, exploratory code, one-offs, etc.
Control of robustness. Gradually narrowing or broadening error handling.
Diverse options. What errors should be signalled? When? By which code? What if consuming code wants to signal that producing code should be more strict? Or more relaxed? What if it's the other way around -- producing code wants to signal that consuming code should be more careful or can relax? What can be done if producing and consuming code have conflicting philosophies? What if producing code cannot be altered (eg it's a library, or written in another language)?
Interoperation between languages / codebases. The only way that can work well is if Raku provides both high levels of control and diverse options.
Convenient refactoring between these scenarios.
All of these factors, and more, underlie Raku's approach to error handling.
CATCH is a really old feature of the language.
It used to only exist inside of a try block.
(Which is not very Rakuish.)
It is also a very rarely used part of Raku.
Which means that not a lot of people have come up with “pain points” of the feature.
So then very rarely has anyone done any work to make it more Rakuish.
Both of those combined make it so that CATCH is a rather featureless part of the language.
If you look at the test file for the feature, you will note that most of it was written in 2009 when the test suite was still a part of the Pugs project.
(And most of the rest are tests for bugs that have been found over the years.)
There is a very good reason that few people have tried to add new behaviours to CATCH, there are plenty of other features that are much nicer to work with.
If you want to replace a result in the event of an exception
sub may-die () {
if Bool.pick {
return 'normal'
} else {
die
}
}
my $result;
{
CATCH { default { $result = 'replacement' }}
$result = may-die();
}
It is much easier to just use try without CATCH, along with defined‑or // to get something that works very similarly.
my $result = try { may-die } // 'replacement';
It is even easier if you are dealing with soft failures instead of hard exceptions, because you can just use defined‑or by itself.
sub may-fail () {
if Bool.pick {
return 'normal'
} else {
fail
}
}
my $result = may-fail() // 'replacement';
In fact the only way to use CATCH with a soft failure is to combine it with try
my $result;
try {
CATCH { default { $result = 'replacement' }}
$result = may-fail();
}
If your soft failure is the base of all failure objects Nil, you can either use // or is default
my $result = may-return-nil // 'replacement';
my $result is default<replacement> = may-return-nil;
But Nil won't just work with CATCH no matter how much you try.
Really the only time I would normally use CATCH is when I want to handle several different errors in different ways.
{
CATCH {
when X::Something { … }
when X::This { … }
when X::That { … }
default { … }
}
# some code that may throw X::This
…
# some code that may throw X::NotSpecified (default)
…
# some code that may throw X::Something
…
# some code that may throw X::This or X::That
…
# some code that may fail instead of throw
# (sunk so that it will throw immediately)
sink may-fail;
}
Or if I wanted to show how you could write this [terrible] Visual Basic line
On Error Resume Next
In Raku
CATCH { default { .resume } }
That of course doesn't really answer your question in the slightest.
You say that you expected CATCH to be removed from the control flow.
The whole point of CATCH is to insert itself into the exceptional control flow.
Actually that's not accurate. It doesn't so much insert itself into the control flow as ending the control flow while doing some processing before moving on to the caller/outside block. Presumably because the data of the current block is in an erroneous state and should no longer be trusted.
That still doesn't explain why your code fails to compile.
You expected CATCH to have its own special syntax rule when it comes to the semicolon ending a statement.
If it worked the way you expected it would fail one of the important [syntax] rules in Raku, “there should be as few special cases as possible”. Its syntax is not special in any way unlike what you seem to expect.
CATCH is just one of many phasers with one important extra bit of functionality, it stops exception propagation down the call stack.
What you seem to be asking for it to instead alter the result of an expression that may throw.
That doesn't seem like a good idea.
$a + may-die() + $b
You want to be able to replace the exception from may-die with a value.
$a + 42 + $b
Basically you are asking for the ability to add action‑at‑a‑distance as a feature.
There is also a problem, what if you actually wanted $a + may‑die to be replaced instead.
42 + $b
There is no way in your idea for you to specify that.
Even worse, there is a way that could accidently happen. What if may‑die started returning a failure instead of exception. Then it would only cause an exception when you tried to use it, for example by adding it to $a.
If some code throws an exception, the block is in an unrecoverable state and it needs to halt execution. This far, no farther.
If an expression throws an exception, the result of executing the statement it is in, is suspect.
Other statements may rely on that broken statement, so then the whole block is also suspect.
I do not think it would be that good of an idea if it instead allowed the code to continue but with a different result for the current expression. Especially if that value can be far removed from the expression somewhere else inside of the block. (action‑at‑a‑distance)
If you could come up with some code that would be vastly improved with .resume(value), then maybe it could be added.
(I personally think that leave(value) would be more useful in such a circumstance.)
I will grant that .resume(value) seems like it may be useful for control exceptions.
(Caught with CONTROL instead of CATCH.)
The previous answer that was posted here
How yield catches StopIteration exception?
and accepted shows the following:
iterator = iter(iterable)
try:
while True:
item = next(iterator)
do_stuff(item)
except StopIteration:
pass
finally:
del iterator
The problem in Python 3.6.4 pass hangs indefinitely never closing out of the program cleanly.
del complains that the iterator is not defined when it is. My assumption is scope changes.
Any ideas how to exit cleanly when StopIteration is raised?
Well I realized the loop expression must live inside the try statement only. I attempted to move the loop expression outside of the try catch block and it does hang indefinitely as well change scope when finally portion is executed.
I am sure there is a more formal answer that breaks down scope, exception and raise, etc. I now know there is only way to do this.
I'm making a custom plugin to query a database for user info to aide customer support. My backend is slack.
Everytime I start the bot command I'm greeted with:
Computer says nooo. See logs for details:
catching classes that do not inherit from BaseException is not allowed
I'm not sure if this is warning me that I'm attempting to catch an exception that isn't a BaseClass in my code or if an unknown exception was raised and caught elsewhere outside of my plugin.
To debug I tried:
try:
do_the_thing()
except (TypeError, ValueError) as e:
return('Something went wrong.')
I also tried:
try:
do_the_thing()
except Exception as e:
return('Something went wrong.')
And I still get the errbot admonition. Note that the command still runs and does the right thing where there is no exception raised by do_the_thing().
It means that:
Somewhere in your code you have an except ... statement where the exception ... (or one of the exceptions in the sequence ...) is not a subclass of BaseException, and
An exception is being thrown that is caught by that except ... statement.
The TypeError can be raised only when an exception is actually thrown because the names you give to except ... must be evaluated for their current values at that time; just because TypeError referenced a particular class at one point in the program's execution doesn't mean it won't be changed later to reference another object (though that would be admittedly perverse).
The Python interpreter should be giving you a full traceback of the exception; the first thing you need to do is find this. It could be occurring in one of two situations. (This is for single-threaded programs; I'm assuming your program is not multithreaded.)
During the execution of your program, in which case the program will be terminated by the exception, or
During finalization of objects (in their __del__(self) functions) in which case the error will be printed to stderr.
In both cases there should be a stack trace, not just the error message; I've confirmed that at least on Python ≥3.4 a stack trace is printed out for case 2.
You then need to follow this stack trace to see where the problem lies. Remember that the names you give to except ... are variables (even things like TypeError) that can be reassigned, so that you could conceivably be dealing with a (perverse) situation like:
TypeError = False
try:
...
except TypeError:
...
But more likely it will be something obvious such as:
class MyException: # Doesn't inherit from Exception
...
try:
...
except MyException:
...
There is one special case you need to be aware of: if you are seeing messages to stderr (case "2. During finalization," above) printed out as your program exits that means that the exception was thrown during cleanup as the interpreter shuts down, where random variables throughout the program may have already been set to None as part of the cleanup process. But in this case your program should still exit successfully.
Exception is not being caught with its name but it is caught when others keyword is used? Below is my code
p.ads
package p is
procedure proc ;
end p;
p.adb
package body p is
my_exp:exception; -- user define exception
procedure proc is
begin
raise my_exp; -- spreading exception
end proc;
end p;
p_main.adb
with p;
with ada.text_io;
use ada.text_io;
use p;
procedure p_main is
begin
proc;
exception
when my_exp=>put(" my_exp");-- error but when others is used then.its good why?
end p_main;
A/c to adacore site my_exp is not visible here then how it is visible when others keyword is used?
Your code has numerous syntax errors. It's always better to copy-and-paste your exact code into the question; you appear to have re-typed it, which makes it difficult to distinguish between typos and actual errors in your original code.
Ignoring the syntax errors, this:
exception
when my_exp => put(" my_exp");
fails to compile because the name my_exp is not visible. (If you want it to be visible it should be in the package specification, but that's not what you asked.)
If you replace when my_exp by when others, it works; the exception is handled. This is because a when others clause handles any exception that's been raised, whether its name is visible or not.
An exception is a condition that exists when a program is running. The exception handler detects and handles that run-time entity. It doesn't need to refer to it by whatever identifier you used to define it.
If the name my_exp had been visible, the handler still (almost certainly) wouldn't use the name to identify the exception. Instead, the compiler creates some run-time data structure that allows exceptions to be identified, perhaps by a reference to a specific memory address. The exact mechanism depends on the compiler, and understanding the details is not terribly important unless you're writing a compiler.
The run-time detection that an exception has been raised, and which exception it is, does not depend on the name you've assigned to the exception in your source code.
The reference to the name my_exp is rejected at compile time because that name is not visible at compile time.
The first thing to understand is that if your package can raise a custom exception which isn't advertised by the package specification, that is bad design because it leads to unpleasant surprises for the package users.
So the right thing to do is to declare my_exp in the package instead of hiding it in the body.
package p is
my_exp:exception; -- user define exception
procedure proc ;
end p;
And then fix all the other trivial syntax errors, and your program works as advertised.
But even if you don't, and "my_exp" is not visible in the main program, its exception handler can identify it correctly.
procedure p_main is
begin
proc;
end p_main;
./p_main
raised P.MY_EXP : p.adb:7
Or you can manipulate that information yourself, for better diagnostics or error recovery
with Ada.Exceptions; -- we need to process exception data
procedure p_main is
begin
proc;
exception
when E : others =>
put("something bad happened : " & Ada.Exceptions.Exception_Name(E));
end p_main;
./p_main
something bad happened : P.MY_EXP
I have been doing some work with python-couchdb and desktopcouch. In one of the patches I submitted I wrapped the db.update function from couchdb. For anyone that is not familiar with python-couchdb the function is the following:
def update(self, documents, **options):
"""Perform a bulk update or insertion of the given documents using a
single HTTP request.
>>> server = Server('http://localhost:5984/')
>>> db = server.create('python-tests')
>>> for doc in db.update([
... Document(type='Person', name='John Doe'),
... Document(type='Person', name='Mary Jane'),
... Document(type='City', name='Gotham City')
... ]):
... print repr(doc) #doctest: +ELLIPSIS
(True, '...', '...')
(True, '...', '...')
(True, '...', '...')
>>> del server['python-tests']
The return value of this method is a list containing a tuple for every
element in the `documents` sequence. Each tuple is of the form
``(success, docid, rev_or_exc)``, where ``success`` is a boolean
indicating whether the update succeeded, ``docid`` is the ID of the
document, and ``rev_or_exc`` is either the new document revision, or
an exception instance (e.g. `ResourceConflict`) if the update failed.
If an object in the documents list is not a dictionary, this method
looks for an ``items()`` method that can be used to convert the object
to a dictionary. Effectively this means you can also use this method
with `schema.Document` objects.
:param documents: a sequence of dictionaries or `Document` objects, or
objects providing a ``items()`` method that can be
used to convert them to a dictionary
:return: an iterable over the resulting documents
:rtype: ``list``
:since: version 0.2
"""
As you can see, this function does not raise the exceptions that have been raised by the couchdb server but it rather returns them in a tuple with the id of the document that we wanted to update.
One of the reviewers went to #python on irc to ask about the matter. In #python they recommended to use sentinel values rather than exceptions. As you can imaging just an approach is not practical since there are lots of possible exceptions that can be received. My questions is, what are the cons of using Exceptions over sentinel values besides that using exceptions is uglier?
I think it is ok to return the exceptions in this case, because some parts of the update function may succeed and some may fail. When you raise the exception, the API user has no control over what succeeded already.
Raising an Exception is a notification that something that was expected to work did not work. It breaks the program flow, and should only be done if whatever is going on now is flawed in a way that the program doesn't know how to handle.
But sometimes you want to raise a little error flag without breaking program flow. You can do this by returning special values, and these values can very well be exceptions.
Python does this internally in one case. When you compare two values like foo < bar, the actual call is foo.__lt__(bar). If this method raises an exception, program flow will be broken, as expected. But if it returns NotImplemented, Python will then try bar.__ge__(foo) instead. So in this case returning the exception rather than raising it is used to flag that it didn't work, but in an expected way.
It's really the difference between an expected error and an unexpected one, IMO.
exceptions intended to be raised. It helps with debugging, handling causes of the errors and it's clear and well-established practise of other developers.
I think looking at the interface of the programme, it's not clear what am I supposed to do with returned exception. raise it? from outside of the chain that actually caused it? it seems a bit convoluted.
I'd suggest, returning docid, new_rev_doc tuple on success and propagating/raising exception as it is. Your approach duplicates success and type of 3rd returned value too.
Exceptions cause the normal program flow to break; then exceptions go up the call stack until they're intercept, or they may reach the top if they aren't. Hence they're employed to mark a really special condition that should be handled by the caller. Raising an exception is useful since the program won't continue if a necessary condition has not been met.
In languages that don't support exceptions (like C) you're often forced to check return values of functions to verify everything went on correctly; otherwise the program may misbehave.
By the way the update() is a bit different:
it takes multiple arguments; some may fail, some may succeed, hence it needs a way to communicate results for each arg.
a previous failure has no relation with operations coming next, e.g. it is not a permanent error
In that situation raising an exception would NOT be usueful in an API. On the other hand, if the connection to the db drops while executing the query, then an exception is the way to go (since it's a permament error and would impact all operations coming next).
By the way if your business logic requires all operations to complete successfully and you don't know what to do when an update fails (i.e. your design says it should never happen), feel free to raise an exception in your own code.