# Need help with exception handling

I have read the docs for Catch, Throw, and friends multiple times, and I confess that I find them utterly incomprehensible...

Can someone tell me how one would port the following Python snippet to Mathematica?

def safeeval(fn, x, epsilon=sys.float_info.epsilon):
try:
# first attempt
return fn(x)
except Exception, first_exception:
try:
try:
# second attempt
return fn(x + epsilon)
except:
# third (and last) attempt
return fn(x - epsilon)
except:
# give up!
raise first_exception


In words: return fn(x), trapping any exceptions during the evaluation of fn(x). If there is an exception, cache it in first_exception, and return fn(x + epsilon), again trapping any exceptions. If there is again an exception (a second one), return fn(x - epsilon). Lastly, if there is again an exception (a third one), give up and re-raise first_exception.

A higher-level, less literal translation of the above would be this: return the first one of fn(x), fn(x + epsilon), and fn(x - epsilon) that evaluates without errors (silencing any errors that do occur along the way). If none of these expressions evaluates without errors, then proceed exactly as with the usual evaluation of fn(x) (emitting the appropriate error messages, etc.).

EDIT: OK, I finally came up with a solution that approximates what I described above, though I'm not sure it's anywhere close to optimal:

safeEval[fn_, x_, epsilon_:$MachineEpsilon]:= Module[{v, sentinel}, v = Quiet[Check[fn[x], Check[fn[x+epsilon], Check[fn[x-epsilon], sentinel]]]]; If[v =!= sentinel, v, fn[x]] ]  I was (*ahem*) thrown off by the whole Catch, Throw nomenclature. (These functions now seem to me like something unnaturally tacked onto Mathematica to make it look more like other more widely known languages.) - ## 2 Answers I don't know Python, and there's one aspect of the Python code I don't get; but this does what the last paragraph describes. safeeval[fn_, x_, epsilon_] := ReleaseHold @ Catch @ Quiet @ Check[ fn[x], Check[ ReleaseHold @ Catch @ Quiet @ Check[ fn[x + epsilon], Throw @ Hold[fn[x - epsilon]]], Throw @ Hold[fn[x]]]]  I don't see how to re-enter the first f[x] from the point of the last Throw without creating an infinite loop. That's the part of the Python code I don't understand: re-raising first_exception. It certainly looks easier in Python. Perhaps someone will come up with a more elegant solution. Examples First try succeeds safeeval[# &, 0, 1] (* 0 *)  Second try succeeds safeeval[1/# &, 0, 1] (* 1 *)  Third try succeeds safeeval[1/(# (# - 1)) &, 0, 1] (* 1/2 *)  Default case - all failed safeeval[1/0 &, 0, 1]  Power::infy: Infinite expression 1/0 encountered. >> (* ComplexInfinity *)  Edit: Cacheing the error messages Applying one of the answers to this question, one can easily adapt the above to cache the error messages from the first evaluation of f[x] and avoid having to evaluate it twice. Module[{messages = {}}, clearMessages[] := messages = {}; collectMessages[m_] := AppendTo[messages, m]; printMessages[] := ReleaseHold @ messages; ]; safeeval[fn_, x_, epsilon_ :$MachineEpsilon] :=
Module[{retval},
ReleaseHold @ Catch @ Quiet @
Check[
clearMessages[];
InternalAddHandler["Message", collectMessages];
retval = fn[x];
InternalRemoveHandler["Message", collectMessages];
retval,
Check[
ReleaseHold @ Catch @ Quiet @
Check[
fn[x + epsilon],
Throw @ Hold[fn[x - epsilon]]],
Throw[Hold[printMessages[]; retval]]
]
]
]

-
I think this would more in accord with the Python code if it were written safeeval[fn_, x_, epsilon_:$MachineEpsilon] := ... – m_goldberg Aug 25 '13 at 22:18 I thought of that, but since I don't know Python, I didn't want to assume what epsilon was. I hoped the OP knew about $MachineEpsilon already. But thanks anyway! (+1 on the comment. :) –  Michael E2 Aug 25 '13 at 22:41
Thanks! I came up with a solution very similar to yours (see my EDIT), though I can't follow your use of Hold, ReleaseHold, etc. I guess that their role has something to do with preserving the whole Catch/Throw scheme? As I alluded to in my EDIT, this scheme seems like something shoehorned into Mathematica for basically PR purposes... –  kjo Aug 25 '13 at 22:45
@kjo The Hold is so that f[x] is not evaluated inside the Quiet. ReleaseHold removes the Hold after the Catch; at that point, f[x] will be evaluated, outside Quiet. == You're solution seems reasonable. One defect (IMO) of both of ours is that f[x] is evaluated twice in the worst case. –  Michael E2 Aug 25 '13 at 23:40
@kjo See update. (The second half of your comment is what I meant about evaluating f[x] twice.) –  Michael E2 Aug 26 '13 at 3:36

It looks like you already have your solution, and a rather elegant one at that, but perhaps what is yet lacking is a convenient method that does not require manually writing that nested structure.

We can use recursion. The basic form looks like this:

SetAttributes[errorTry, HoldAll]

errorTry[a_, b__] := Quiet @ Check[a, errorTry[b]]

errorTry[x_] := x


You would use it like this:

fn[1] := (1/0; 1)
fn[2] := (1/0; 2)
fn[3] := (1/0; 3)

errorTry[fn[1], fn[2], fn[3], fn[1]]

1


With this simple form you do have to specify the first expression twice, and it is also evaluated twice in the fall-through case. To address these issues we might use:

SetAttributes[errorTry2, HoldAll]

errorTry2[a_, b___] := Module[{x}, errorTry[x = a, b, x]]


Use:

errorTry2[fn[1], fn[2], fn[3]]


If you wish to return the error messages for fn[1] in fall-through you could either simply reevaluate that expression, or if that is costly you can cache. The simple approach would look something like this:

SetAttributes[{errorTry3, eT3}, HoldAll]

errorTry3[a_, b___] := eT3[a, b] /. eT3[] :> a

eT3[a_, b___] := Quiet @ Check[a, eT3[b]]


Use as above. Finally, the complete self-contained function with caching:

SetAttributes[errorTry4, HoldAll]

errorTry4[a_, b___] :=
Module[{f, x, msg = {}},
SetAttributes[f, HoldAll];
f[i_, j___] := Quiet @ Check[i, f[j]];
Block[{Message = AppendTo[msg, {##}] &}, x = a];
If[msg === {}, x, f[b] /. f[] :> (Message @@@ msg; x)]
]


I hope this gives you all the options you could want.

-
There are a lot of very cool ideas in that reply. Thanks! –  kjo Aug 26 '13 at 13:02
@kjo You're welcome. –  Mr.Wizard Aug 26 '13 at 15:45