0

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<_>
asked Jul 19, 2021 at 20:59

1 Answer 1

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() with raise error, since Exception.Reraise isn'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 retry with all three arguments (I used () as the "event"), and then run the resulting async computation synchronously. Maybe you weren't doing that?
  • You might want to look into using Async.Catch for handling async exceptions instead.
answered Jul 19, 2021 at 22:17
Sign up to request clarification or add additional context in comments.

4 Comments

it's recommended to use rethrow() or reraise() instead of raise exception , because latter changes stacktrace
Actually, reraise 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.
So I think I have to apologize, it works indeed. My test case running this code had the bug :D This remind me of a question; Who gonna test your tests? Thank You @brianberns for Your time on that.
Thx for Async.Catch - didn't know that one! Will check it.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.