I'm using a stupidly simple example here to avoid confusion with details of the real case, and also to provide a minimal complete example. The code here can be pasted into Visual Studio, Visual Studio Code or LinqPad, as long as you have referenced the LanguageExt.Core Nuget package, and have added the following using
s...
using LanguageExt;
using static LanguageExt.Prelude;
Imagine I have an API that take a float
, and returns the square root doubled. I have helper functions as follows...
static Either<string, double> Sqrt(double d) =>
d >= 0.0
? Math.Sqrt(d)
: "Error: Negative number";
static Either<string, double> Double(double d) =>
d > 10.0
? "Error: Too big to double"
: 2 * d;
My API endpoint returns a string of the form "Result: 23.3" if successful, or an error if not.
I wire these together as follows...
public static string Calc(double d) =>
(from res1 in Sqrt(d)
from res2 in Double(res1)
select res2)
.Match(res => $"Result: {res}",
err => err);
This works as expected. Obviously, my real code would have more from
clauses than this, but I'm trying to keep this simple.
Now, suppose (which is more realistic) the helper functions were async
, then I would need to modify my endpoint to look like this...
public static async Task<string> Calc(double d) =>
await (from res1 in Sqrt(d).ToAsync()
from res2 in Double(res1).ToAsync()
select res2)
.Match(res => $"Result: {res}",
err => err);
Again, all works.
Aside: I picked up the coding style above from reading some of Paul Louth's code in the LanguageExt issues. If there is a better way of doing this, please feel free to suggest it. I don't claim to be any kind of expert with this stuff.
However, now in the case of errors, I want to log them, send an email, etc, so the second lambda to the Match
method needs to be async
. In order to avoid a compiler error, I need to make the first async
as well, and then await
the result...
public static async Task<string> CalcAsync2(double d) =>
await await (from res1 in Sqrt(d).ToAsync()
from res2 in Double(res1).ToAsync()
select res2)
.Match(async res => $"Result: {res}",
async err => {
// Log the error, send an email, etc (async);
return err;
});
This starts to look like I'm just doing this completely wrongly. Maybe I'm wrong, but awaiting an awaited task doesn't seem right.
Anyone able to comment here? I want to be able to log the error if any of the helper methods returned a Left
, but would prefer it if all the async
work (and indeed, as much of the work as possible) were in the seqeunce of from
cluses, leaving the lambdas in Math
to do little more than return values.
Just use MatchAsync
:
static Task<string> CalcAsync2(double d) =>
(from res1 in Sqrt(d).ToAsync()
from res2 in Double(res1).ToAsync()
select res2)
.MatchAsync(res => Task.FromResult($"Result: {res}"),
async err =>
{
await Task.Delay(1);
// Log the error, send an email, etc (async);
return err;
}
);