【问题标题】:language-ext Task of Either with multiple from clauses具有多个 from 子句的 Either 的语言-ext 任务
【发布时间】:2019-02-14 07:15:09
【问题描述】:

我正在使用语言扩展学习 FP,但遇到了一个我无法克服的问题。我将代码简化为以下示例:

using System;
using System.Threading.Tasks;
using LanguageExt;
using static LanguageExt.Prelude;
using Xunit;

namespace Temp {

    public class SelectManyError {

        [Fact]
        public async Task Do() {

            var six = await from a in Task.FromResult(Right<Exception, int>(1))
                            from b in Task.FromResult(Right<Exception, int>(2))
                            from c in Task.FromResult(Right<Exception, int>(3))
                            select a + b + c;

        }
    }
}

我收到此错误:

为源类型Task&lt;Either&lt;Exception, int&gt;&gt; 找到了查询模式的多个实现。对“SelectMany”的模糊调用。

通过阅读this webpage,我了解编译器认为问题所在。但是,我显然遗漏或不理解一些重要的东西,因为我无法弄清楚这个错误是如何由这种情况引起的,或者如何处理它。如果它只有 2 个 from 子句,这将工作得很好,这让我更加困惑。

这是解决此类问题的错误方法吗?还有其他我不知道的方法吗?

【问题讨论】:

  • 您使用的是什么版本的 Language Ext?
  • 我使用的是 3.0.19 版本
  • > "我编辑了整个文件,现在是否给你同样的错误?"感谢您提供更详细的示例。是的,我现在遇到和你一样的错误。

标签: language-ext


【解决方案1】:

Lang-ext 作者在这里。我们一直在lang-ext github repo 上讨论这个问题。

这些是我的cmets:

这很难。老实说,它们并不是真正的误报,因为Either&lt;L, R&gt; 支持+ 运算符,因此属于Task&lt;R&gt;SelectMany 将产生一个有效结果,就像与@987654327 一起使用的SelectMany @。

基本上abc 值可以合法地为intEither&lt;Exception, int&gt;,具体取决于编译器选择的SelectMany 实现。

整个表达式对所有SelectMany 扩展都有效,这显然是我们有这种歧义的原因。

遗憾的是,将 var three = ... 更改为 Either&lt;Exception, int&gt; three = ... 并没有改变推理系统。因为这是编译器混淆的两个可能表达式之间的主要区别。

您可能想要做的一件事是使用OptionAsync&lt;A&gt; 而不是Task&lt;Either&lt;L, R&gt;&gt; 而不是Task&lt;Either&lt;L, R&gt;&gt;,而是使用EitherAsync&lt;L, R&gt;。它们本质上是完全相同的类型,只是它很好地包装了所有的绑定语义,所以你再也不会遇到这个问题了。

我正在为 lang-ext 中的所有 monadic 类型创建一个 *Async 变体。为了方便,潜在的性能优势,并允许相当于 3 个嵌套级别的 monad:M&lt;A&lt;B&lt;C&gt;&gt;&gt; 例如 Seq&lt;OptionAsync&lt;A&gt;&gt;Seq&lt;Task&lt;Option&lt;A&gt;&gt;&gt; 相同。

无论如何,回到你上面的例子,你可以这样做:

public async Task<int> Method()
{
    var six = from a in Right<Exception, int>(1).ToAsync()
              from b in Right<Exception, int>(2).ToAsync()
              from c in Right<Exception, int>(3).ToAsync()
              select a + b + c;

    return await six.IfLeft(0);
}

或者如果你想从Task&lt;int&gt;构造:

public async Task<int> Method()
{
    var six = from a in RightAsync<Exception, int>(Task.FromResult(1))
              from b in RightAsync<Exception, int>(Task.FromResult(2))
              from c in RightAsync<Exception, int>(Task.FromResult(3))
              select a + b + c;

     return await six.IfLeft(0);
}

或者,您可以留在 monad 中并返回:

public EitherAsync<Exception, int> Method() =>
    from a in RightAsync<Exception, int>(Task.FromResult(1))
    from b in RightAsync<Exception, int>(Task.FromResult(2))
    from c in RightAsync<Exception, int>(Task.FromResult(3))
    select a + b + c;

【讨论】:

    【解决方案2】:

    编译器很难理解a 的类型应该是什么(intEither&lt;Exception, int&gt;),因为它在第二个from 行中未使用。

    对于这种特定情况,这是一个非常丑陋的解决方法。但是,对于任何类型,我认为该 hack 可以适用于该类型。

    using System;
    using System.Threading.Tasks;
    using LanguageExt;
    using Xunit;
    using static LanguageExt.Prelude;
    
    public class Namespace
    {
        [Fact]
        public async Task Method()
        {
            var six = await from a in Right<Exception, int>(1).AsTask()
                            from b in Right<Exception, int>(a - a + 2).AsTask()
                            from c in Right<Exception, int>(3).AsTask()
                            select a + b + c;
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-08
    • 2019-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多