Strange things... I just wanted to do a simple retry on exceptions in F# but the catch doesn't catch :) Any ideas?
let rec retry times next event =
async {
try
return! next event
with
| _ when times > 0 -> return! retry (times - 1) next event
| error -> return error.Reraise()
}
if the next is a function like;
let handler evt = async { failwith "Oh-no" }
Then the code in try executes but it is not catched. What is going on? :O
UPDATE
The reraise is an extension method as described here: https://github.com/fsharp/fslang-suggestions/issues/660 by nikonthethird.
type Exception with
member this.Reraise () =
(ExceptionDispatchInfo.Capture this).Throw ()
Unchecked.defaultof<_>
1 Answer 1
Your code does catch the exceptions. Here's the full program I'm running to test it:
let rec retry times next event =
async {
try
printfn "Retry: %A" times
return! next event
with
| _ when times > 0 -> return! retry (times - 1) next event
| error -> raise error
}
let handler evt =
async {
printfn "About to fail once"
failwith "Oh-no"
}
[<EntryPoint>]
let main argv =
retry 3 handler ()
|> Async.RunSynchronously
|> printfn "%A"
0
Output:
Retry: 3
About to fail once
Retry: 2
About to fail once
Retry: 1
About to fail once
Retry: 0
About to fail once
Unhandled exception. System.Exception: Oh-no
You can see that the exceptions are being caught, because handler is invoked multiple times before retry gives up.
Notes:
- I replaced
return error.Reraise()withraise error, sinceException.Reraiseisn't a defined method. I'm not sure what you had in mind here, but it doesn't directly affect the answer to your question. - It's important to fully invoke
retrywith all three arguments (I used()as the "event"), and then run the resultingasynccomputation synchronously. Maybe you weren't doing that? - You might want to look into using
Async.Catchfor handling async exceptions instead.
4 Comments
rethrow() or reraise() instead of raise exception , because latter changes stacktracereraise isn't allowed here, presumably because it's inside a computation expression. (The compiler error isn't clear: Calls to 'reraise' may only occur directly in a handler of a try-with.) In any case, as I mentioned, it's not directly relevant to OP's question.