Given the F# task
computation expression I can write:-
task {
try
let! accessToken = getAccessTokenAsync a b
try
let! resource = getResourceAsync accessToken uri
// do stuff
with
| ex -> printfn "Failed to get API resource. %s" ex.Message
with
| ex -> printfn "Failed to get access token. %s" ex.Message
return ()
}
but what I want to do is have non-nested exception handling around the two getBlahAsync
function calls. This can be done in C# quite easily in an async
method with multiple await
s.
How to do so in an F# computation expression? If I try it in the simple and obvious way, accessToken
from the first try..with
doesn't flow into the second try..with
.
(The trouble with nesting is that the // do stuff
section could grow a bit, pushing the outer with
further and further away from its try
.)
How to do it in C#:-
static async Task MainAsync()
{
String accessToken = null;
try
{
accessToken = await GetAccessTokenAsync("e", "p");
}
catch (Exception ex)
{
Console.Error.WriteLine("Failed to get access token. " + ex.Message);
return;
}
String resource = null;
try
{
resource = await GetResourceAsync(accessToken);
}
catch (Exception ex)
{
Console.Error.WriteLine("Failed to get API resource. " + ex.Message);
return;
}
// do stuff
}
The main problem with translating the C# code is that F# does not let you use return
to jump out of the function body early. You can avoid nesting exceptions in various ways, but you will not be able to return early. This can be implemented as another computatione expression, but that's more of a curiosity than something you'd actually want to use here.
My recommendation would be to just split the function into one that gets all the resources and handles exceptions and another one that does the stuff. That does not eliminate nesting, but it will make the code fairly readable.
let doStuff accessToken resource = task {
// do stuff
}
let getResourcesAndDoStuff a b uri = task {
try
let! accessToken = getAccessTokenAsync a b
try
let! resource = getResourceAsync accessToken uri
return! doStuff accessToken resource
with ex ->
printfn "Failed to get API resource. %s" ex.Message
with ex ->
printfn "Failed to get access token. %s" ex.Message
}
As an aside, do you have some particular reason for using task
rather than the normal built-in F# async
workflow? It is not necessarily a problem, but async
composes better and supports cancellation, so it is often a sensible default choice.