Jinx
Jinx

Reputation: 358

F# async try with not catching exceptions

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<_>

Upvotes: 0

Views: 418

Answers (1)

Brian Berns
Brian Berns

Reputation: 17038

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.

Upvotes: 1

Related Questions