【问题标题】:Chain asynchronous operations that return Either using Language-Ext in C#在 C# 中使用 Language-Ext 链接返回 Either 的异步操作
【发布时间】:2018-07-29 16:46:48
【问题描述】:

我正在为 C# 使用 Language-Ext 库,并且我正在尝试链接返回 Either 类型的异步操作。假设我有三个函数,如果它们成功则返回一个整数,如果失败则返回一个字符串,另一个函数将前三个函数的结果相加。在下面的示例实现中,Op3 失败并返回一个字符串。

public static async Task<Either<string, int>> Op1()
{
    return await Task.FromResult(1);
}

public static async Task<Either<string, int>> Op2()
{
    return await Task.FromResult(2);
}

public static async Task<Either<string, int>> Op3()
{
    return await Task.FromResult("error");
}

public static async Task<Either<string, int>> Calculate(int x, int y, int z)
{
    return await Task.FromResult(x + y + z);
}

我想链接这些操作,我正在尝试这样做:

var res = await (from x in Op1()
                 from y in Op2()
                 from z in Op3()
                 from w in Calculate(x, y, z)
                 select w);

但是我们的代码无法编译,因为我收到了 cannot convert from 'LanguageExt.Either&lt;string, int&gt;' to 'int' 的错误 Calculate 的参数。我应该如何链接这些函数?

【问题讨论】:

  • 您的 Op2 和 Op3 方法接受参数,但没有提供任何参数?这是复制粘贴错误还是您的代码的实际问题。我在本地测试了您的代码,并在我为 Op2 和 Op3 提供参数后成功构建。
  • 确实是一个错字。当Op2Op3 没有参数时,就会出现问题。

标签: c# functional-programming async-await either language-ext


【解决方案1】:

问题在于 LINQ 查询无法确定要使用哪个版本的 SelectMany,因为第二行中没有使用 x。您可以通过将 Task&lt;Either&lt;L, R&gt;&gt; 转换为 EitherAsync&lt;L, R&gt; 来解决此问题:

    public static async Task<int> M()
    {
        var res = from x in Op1().ToAsync()
                  from y in Op2().ToAsync()
                  from z in Op3().ToAsync()
                  from w in Calculate(x, y, z).ToAsync()
                  select w;

        return await res.IfLeft(0);
    }

或者,不是返回Task&lt;Either&lt;L, R&gt;&gt;,而是返回EitherAsync&lt;L, R&gt;

    public static EitherAsync<string, int> Op1() =>
        1;

    public static EitherAsync<string, int> Op2() =>
        2;

    public static EitherAsync<string, int> Op3() =>
        3;

    public static EitherAsync<string, int> Calculate(int x, int y, int z) =>
        x + y + z;

    public static async Task<int> M()
    {
        var res = from x in Op1()
                  from y in Op2()
                  from z in Op3()
                  from w in Calculate(x, y, z)
                  select w;

        return await res.IfLeft(0);
    }

【讨论】:

    【解决方案2】:

    x、y 和 z 不是 int 类型,而是 Either&lt;string, int&gt; 更改 Calculate(int x, int y, int z) 以接受 Either 调用实例:Calculate(Either&lt;string, int&gt; x, Either&lt;string, int&gt; y, Either&lt;string, int&gt;z),或通过

    x.{The int getter property}
    

    【讨论】:

    • 我认为这不是问题所在。如果我将所有函数同步(删除所有Taskasyncawait),则类型被正确推断,如果我运行它,当Op3 返回字符串时计算将中止(如预期的那样) .在这种情况下,所有值都从 monad 中正确解包。我怀疑这个问题与Either 被包裹在Task 中这一事实有关,但我没有让它工作。
    • @Radin 是正确的。 from .. in 语法只解开一种单子类型。这就是代码的同步版本有效的原因...因为您手动删除了Task&lt;&gt; monad,而from..in 语法将Either&lt;string, int&gt; 解包为int。我看到两个解决方案。您可以嵌套调用MapT(一次解开两个单子类型),或者您可以升级到当前的预发布版本并将嵌套的单子类型Task&lt;Either&lt;,&gt;&gt;替换为单单子类型EitherAsync&lt;,&gt;
    • @TysonWilliams 我想我知道使用MapT 可以如何工作,但我看不到它可以使用EitherAsync&lt;,&gt;。如果我将函数签名中的async Task&lt;Either&lt;,&gt; 替换为EitherAsync&lt;,&gt;,那么我不能在方法中使用await 任务。我可以返回一个任务,但我不能await。我错过了什么吗? (PS:我已经尝试过库的预发布版本)。
    • 不要改变你使用asyncawait的方式。
    • 如果我保留asyncawait,那么编译器会警告我The return type of an async method must be void, Task, Task&lt;T&gt;, ValueTask&lt;T&gt; or another tasklike type。我使用的是 2.2.11-beta 版本。
    猜你喜欢
    • 2021-09-08
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 2018-11-23
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多